Merge branch 'master' into advanced-permissions-phase-1
Этот коммит содержится в:
@@ -37,7 +37,8 @@ func TestGetImage(t *testing.T) {
|
||||
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.ServiceSettings.ImageProxyType = model.NewString("willnorris/imageproxy")
|
||||
cfg.ServiceSettings.ImageProxyType = model.NewString("atmos/camo")
|
||||
cfg.ServiceSettings.ImageProxyOptions = model.NewString("foo")
|
||||
cfg.ServiceSettings.ImageProxyURL = model.NewString("https://proxy.foo.bar")
|
||||
})
|
||||
|
||||
@@ -48,5 +49,5 @@ func TestGetImage(t *testing.T) {
|
||||
resp, err = th.Client.HttpClient.Do(r)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusFound, resp.StatusCode)
|
||||
assert.Equal(t, "https://proxy.foo.bar//"+originURL, resp.Header.Get("Location"))
|
||||
assert.Equal(t, "https://proxy.foo.bar/004afe2ef382eb5f30c4490f793f8a8c5b33d8a2/687474703a2f2f666f6f2e6261722f62617a2e676966", resp.Header.Get("Location"))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
@@ -447,34 +450,40 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
var payload io.Reader
|
||||
var err *model.AppError
|
||||
incomingWebhookPayload := &model.IncomingWebhookRequest{}
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if strings.Split(contentType, "; ")[0] == "application/x-www-form-urlencoded" {
|
||||
payload = strings.NewReader(r.FormValue("payload"))
|
||||
} else {
|
||||
payload = r.Body
|
||||
}
|
||||
payload := strings.NewReader(r.FormValue("payload"))
|
||||
|
||||
if c.App.Config().LogSettings.EnableWebhookDebugging {
|
||||
var err error
|
||||
payload, err = utils.InfoReader(
|
||||
payload,
|
||||
utils.T("api.webhook.incoming.debug"),
|
||||
)
|
||||
incomingWebhookPayload, err = decodePayload(payload)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.debug.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
} else if strings.HasPrefix(contentType, "multipart/form-data") {
|
||||
r.ParseMultipartForm(0)
|
||||
|
||||
decoder := schema.NewDecoder()
|
||||
err := decoder.Decode(incomingWebhookPayload, r.PostForm)
|
||||
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("incomingWebhook", "api.webhook.incoming.error", nil, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
incomingWebhookPayload, err = decodePayload(r.Body)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
parsedRequest, decodeError := model.IncomingWebhookRequestFromJson(payload)
|
||||
|
||||
if decodeError != nil {
|
||||
c.Err = decodeError
|
||||
return
|
||||
if c.App.Config().LogSettings.EnableWebhookDebugging {
|
||||
l4g.Debug(utils.T("api.webhook.incoming.debug"), incomingWebhookPayload.ToJson())
|
||||
}
|
||||
|
||||
err := c.App.HandleIncomingWebhook(id, parsedRequest)
|
||||
err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -499,3 +508,14 @@ func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) {
|
||||
decodeError := &model.AppError{}
|
||||
incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload)
|
||||
|
||||
if decodeError != nil {
|
||||
return nil, decodeError
|
||||
}
|
||||
|
||||
return incomingWebhookPayload, nil
|
||||
}
|
||||
|
||||
@@ -817,6 +817,12 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod
|
||||
}
|
||||
}
|
||||
|
||||
if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id); err != nil {
|
||||
return err
|
||||
} else if _, err = a.addUserToChannel(user, defaultChannel, member); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.ImportUserChannels(user, team, member, tdata.Channels); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (a *App) SetLicense(license *model.License) bool {
|
||||
}
|
||||
|
||||
a.licenseValue.Store((*model.License)(nil))
|
||||
a.SetClientLicense(map[string]string{"IsLicensed": "false"})
|
||||
a.clientLicenseValue.Store(map[string]string(nil))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -148,8 +148,10 @@ func (a *App) SetClientLicense(m map[string]string) {
|
||||
}
|
||||
|
||||
func (a *App) ClientLicense() map[string]string {
|
||||
clientLicense, _ := a.clientLicenseValue.Load().(map[string]string)
|
||||
return clientLicense
|
||||
if clientLicense, _ := a.clientLicenseValue.Load().(map[string]string); clientLicense != nil {
|
||||
return clientLicense
|
||||
}
|
||||
return map[string]string{"IsLicensed": "false"}
|
||||
}
|
||||
|
||||
func (a *App) RemoveLicense() *model.AppError {
|
||||
|
||||
74
app/post.go
74
app/post.go
@@ -6,12 +6,11 @@ package app
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -727,23 +726,68 @@ func (a *App) GetFileInfosForPost(postId string, readFromMaster bool) ([]*model.
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (a *App) GetOpenGraphMetadata(url string) *opengraph.OpenGraph {
|
||||
func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph {
|
||||
og := opengraph.NewOpenGraph()
|
||||
|
||||
res, err := a.HTTPClient(false).Get(url)
|
||||
res, err := a.HTTPClient(false).Get(requestURL)
|
||||
if err != nil {
|
||||
l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", url, err.Error())
|
||||
l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", requestURL, err.Error())
|
||||
return og
|
||||
}
|
||||
defer consumeAndClose(res)
|
||||
|
||||
if err := og.ProcessHTML(res.Body); err != nil {
|
||||
l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", url, err.Error())
|
||||
l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", requestURL, err.Error())
|
||||
}
|
||||
|
||||
makeOpenGraphURLsAbsolute(og, requestURL)
|
||||
|
||||
return og
|
||||
}
|
||||
|
||||
func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) {
|
||||
parsedRequestURL, err := url.Parse(requestURL)
|
||||
if err != nil {
|
||||
l4g.Warn("makeOpenGraphURLsAbsolute failed to parse url=%v", requestURL)
|
||||
return
|
||||
}
|
||||
|
||||
makeURLAbsolute := func(resultURL string) string {
|
||||
if resultURL == "" {
|
||||
return resultURL
|
||||
}
|
||||
|
||||
parsedResultURL, err := url.Parse(resultURL)
|
||||
if err != nil {
|
||||
l4g.Warn("makeOpenGraphURLsAbsolute failed to parse result url=%v", resultURL)
|
||||
return resultURL
|
||||
}
|
||||
|
||||
if parsedResultURL.IsAbs() {
|
||||
return resultURL
|
||||
}
|
||||
|
||||
return parsedRequestURL.ResolveReference(parsedResultURL).String()
|
||||
}
|
||||
|
||||
og.URL = makeURLAbsolute(og.URL)
|
||||
|
||||
for _, image := range og.Images {
|
||||
image.URL = makeURLAbsolute(image.URL)
|
||||
image.SecureURL = makeURLAbsolute(image.SecureURL)
|
||||
}
|
||||
|
||||
for _, audio := range og.Audios {
|
||||
audio.URL = makeURLAbsolute(audio.URL)
|
||||
audio.SecureURL = makeURLAbsolute(audio.SecureURL)
|
||||
}
|
||||
|
||||
for _, video := range og.Videos {
|
||||
video.URL = makeURLAbsolute(video.URL)
|
||||
video.SecureURL = makeURLAbsolute(video.SecureURL)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DoPostAction(postId string, actionId string, userId string) *model.AppError {
|
||||
pchan := a.Srv.Store.Post().GetSingle(postId)
|
||||
|
||||
@@ -897,18 +941,6 @@ func (a *App) ImageProxyAdder() func(string) string {
|
||||
mac.Write([]byte(url))
|
||||
digest := hex.EncodeToString(mac.Sum(nil))
|
||||
return proxyURL + digest + "/" + hex.EncodeToString([]byte(url))
|
||||
case "willnorris/imageproxy":
|
||||
options := strings.Split(options, "|")
|
||||
if len(options) > 1 {
|
||||
mac := hmac.New(sha256.New, []byte(options[1]))
|
||||
mac.Write([]byte(url))
|
||||
digest := base64.URLEncoding.EncodeToString(mac.Sum(nil))
|
||||
if options[0] == "" {
|
||||
return proxyURL + "s" + digest + "/" + url
|
||||
}
|
||||
return proxyURL + options[0] + ",s" + digest + "/" + url
|
||||
}
|
||||
return proxyURL + options[0] + "/" + url
|
||||
}
|
||||
|
||||
return url
|
||||
@@ -931,12 +963,6 @@ func (a *App) ImageProxyRemover() (f func(string) string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
case "willnorris/imageproxy":
|
||||
if strings.HasPrefix(url, proxyURL) {
|
||||
if slash := strings.IndexByte(url[len(proxyURL):], '/'); slash >= 0 {
|
||||
return url[len(proxyURL)+slash+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return url
|
||||
|
||||
132
app/post_test.go
132
app/post_test.go
@@ -8,9 +8,11 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dyatlov/go-opengraph/opengraph"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -245,38 +247,27 @@ func TestImageProxy(t *testing.T) {
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765",
|
||||
},
|
||||
"willnorris/imageproxy": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "x1000",
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/x1000/http://mydomain.com/myimage",
|
||||
},
|
||||
"willnorris/imageproxy_SameSite": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
"atmos/camo_SameSite": {
|
||||
ProxyType: "atmos/camo",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "foo",
|
||||
ImageURL: "http://mymattermost.com/myimage",
|
||||
ProxiedImageURL: "http://mymattermost.com/myimage",
|
||||
},
|
||||
"willnorris/imageproxy_PathOnly": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
"atmos/camo_PathOnly": {
|
||||
ProxyType: "atmos/camo",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "foo",
|
||||
ImageURL: "/myimage",
|
||||
ProxiedImageURL: "/myimage",
|
||||
},
|
||||
"willnorris/imageproxy_EmptyImageURL": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
"atmos/camo_EmptyImageURL": {
|
||||
ProxyType: "atmos/camo",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "foo",
|
||||
ImageURL: "",
|
||||
ProxiedImageURL: "",
|
||||
},
|
||||
"willnorris/imageproxy_WithSigning": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "x1000|foo",
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/x1000,sbhHVoG5d60UvnNtGh6Iy6x4PaMmnsh8JfZ7JfErKjGU=/http://mydomain.com/myimage",
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
@@ -303,25 +294,92 @@ func TestImageProxy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var imageProxyBenchmarkSink *model.Post
|
||||
func TestMakeOpenGraphURLsAbsolute(t *testing.T) {
|
||||
for name, tc := range map[string]struct {
|
||||
HTML string
|
||||
RequestURL string
|
||||
URL string
|
||||
ImageURL string
|
||||
}{
|
||||
"absolute URLs": {
|
||||
HTML: `
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="https://example.com/apps/mattermost">
|
||||
<meta property="og:image" content="https://images.example.com/image.png">
|
||||
</head>
|
||||
</html>`,
|
||||
RequestURL: "https://example.com",
|
||||
URL: "https://example.com/apps/mattermost",
|
||||
ImageURL: "https://images.example.com/image.png",
|
||||
},
|
||||
"URLs starting with /": {
|
||||
HTML: `
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="/apps/mattermost">
|
||||
<meta property="og:image" content="/image.png">
|
||||
</head>
|
||||
</html>`,
|
||||
RequestURL: "http://example.com",
|
||||
URL: "http://example.com/apps/mattermost",
|
||||
ImageURL: "http://example.com/image.png",
|
||||
},
|
||||
"HTTPS URLs starting with /": {
|
||||
HTML: `
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="/apps/mattermost">
|
||||
<meta property="og:image" content="/image.png">
|
||||
</head>
|
||||
</html>`,
|
||||
RequestURL: "https://example.com",
|
||||
URL: "https://example.com/apps/mattermost",
|
||||
ImageURL: "https://example.com/image.png",
|
||||
},
|
||||
"missing image URL": {
|
||||
HTML: `
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="/apps/mattermost">
|
||||
</head>
|
||||
</html>`,
|
||||
RequestURL: "http://example.com",
|
||||
URL: "http://example.com/apps/mattermost",
|
||||
ImageURL: "",
|
||||
},
|
||||
"relative URLs": {
|
||||
HTML: `
|
||||
<html>
|
||||
<head>
|
||||
<meta property="og:url" content="index.html">
|
||||
<meta property="og:image" content="../resources/image.png">
|
||||
</head>
|
||||
</html>`,
|
||||
RequestURL: "http://example.com/content/index.html",
|
||||
URL: "http://example.com/content/index.html",
|
||||
ImageURL: "http://example.com/resources/image.png",
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
og := opengraph.NewOpenGraph()
|
||||
if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
func BenchmarkPostWithProxyRemovedFromImageURLs(b *testing.B) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
makeOpenGraphURLsAbsolute(og, tc.RequestURL)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.ServiceSettings.ImageProxyType = model.NewString("willnorris/imageproxy")
|
||||
cfg.ServiceSettings.ImageProxyOptions = model.NewString("x1000|foo")
|
||||
cfg.ServiceSettings.ImageProxyURL = model.NewString("https://127.0.0.1")
|
||||
})
|
||||
if og.URL != tc.URL {
|
||||
t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL)
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
Message: "",
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
imageProxyBenchmarkSink = th.App.PostWithProxyAddedToImageURLs(post)
|
||||
if len(og.Images) > 0 {
|
||||
if og.Images[0].URL != tc.ImageURL {
|
||||
t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL)
|
||||
}
|
||||
} else if tc.ImageURL != "" {
|
||||
t.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ import (
|
||||
_ "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
_ "github.com/tylerb/graceful"
|
||||
_ "gopkg.in/olivere/elastic.v5"
|
||||
|
||||
// Temp imports for new dependencies
|
||||
_ "github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
@@ -132,11 +133,21 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
|
||||
|
||||
a.EnsureDiagnosticId()
|
||||
|
||||
go runSecurityJob(a)
|
||||
go runDiagnosticsJob(a)
|
||||
go runSessionCleanupJob(a)
|
||||
go runTokenCleanupJob(a)
|
||||
go runCommandWebhookCleanupJob(a)
|
||||
a.Go(func() {
|
||||
runSecurityJob(a)
|
||||
})
|
||||
a.Go(func() {
|
||||
runDiagnosticsJob(a)
|
||||
})
|
||||
a.Go(func() {
|
||||
runSessionCleanupJob(a)
|
||||
})
|
||||
a.Go(func() {
|
||||
runTokenCleanupJob(a)
|
||||
})
|
||||
a.Go(func() {
|
||||
runCommandWebhookCleanupJob(a)
|
||||
})
|
||||
|
||||
if complianceI := a.Compliance; complianceI != nil {
|
||||
complianceI.StartComplianceDailyJob()
|
||||
@@ -166,6 +177,8 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan
|
||||
a.Jobs.StartSchedulers()
|
||||
}
|
||||
|
||||
notifyReady()
|
||||
|
||||
// wait for kill signal before attempting to gracefully shutdown
|
||||
// the running service
|
||||
signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
@@ -236,6 +249,35 @@ func doDiagnostics(a *app.App) {
|
||||
}
|
||||
}
|
||||
|
||||
func notifyReady() {
|
||||
// If the environment vars provide a systemd notification socket,
|
||||
// notify systemd that the server is ready.
|
||||
systemdSocket := os.Getenv("NOTIFY_SOCKET")
|
||||
if systemdSocket != "" {
|
||||
l4g.Info("Sending systemd READY notification.")
|
||||
|
||||
err := sendSystemdReadyNotification(systemdSocket)
|
||||
if err != nil {
|
||||
l4g.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendSystemdReadyNotification(socketPath string) error {
|
||||
msg := "READY=1"
|
||||
addr := &net.UnixAddr{
|
||||
Name: socketPath,
|
||||
Net: "unixgram",
|
||||
}
|
||||
conn, err := net.DialUnix(addr.Net, nil, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
_, err = conn.Write([]byte(msg))
|
||||
return err
|
||||
}
|
||||
|
||||
func doTokenCleanup(a *app.App) {
|
||||
a.Srv.Store.Token().Cleanup()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -70,3 +71,66 @@ func TestRunServerInvalidConfigFile(t *testing.T) {
|
||||
err = runServer(unreadableConfigFile.Name(), th.disableConfigWatch, th.interruptChan)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRunServerSystemdNotification(t *testing.T) {
|
||||
th := SetupServerTest()
|
||||
defer th.TearDownServerTest()
|
||||
|
||||
// Get a random temporary filename for using as a mock systemd socket
|
||||
socketFile, err := ioutil.TempFile("", "mattermost-systemd-mock-socket-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
socketPath := socketFile.Name()
|
||||
os.Remove(socketPath)
|
||||
|
||||
// Set the socket path in the process environment
|
||||
originalSocket := os.Getenv("NOTIFY_SOCKET")
|
||||
os.Setenv("NOTIFY_SOCKET", socketPath)
|
||||
defer os.Setenv("NOTIFY_SOCKET", originalSocket)
|
||||
|
||||
// Open the socket connection
|
||||
addr := &net.UnixAddr{
|
||||
Name: socketPath,
|
||||
Net: "unixgram",
|
||||
}
|
||||
connection, err := net.ListenUnixgram("unixgram", addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer connection.Close()
|
||||
defer os.Remove(socketPath)
|
||||
|
||||
// Listen for socket data
|
||||
socketReader := make(chan string)
|
||||
go func(ch chan string) {
|
||||
buffer := make([]byte, 512)
|
||||
count, err := connection.Read(buffer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data := buffer[0:count]
|
||||
ch<- string(data)
|
||||
}(socketReader)
|
||||
|
||||
// Start and stop the server
|
||||
err = runServer(th.configPath, th.disableConfigWatch, th.interruptChan)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure the notification has been sent on the socket and is correct
|
||||
notification := <-socketReader
|
||||
require.Equal(t, notification, "READY=1")
|
||||
}
|
||||
|
||||
func TestRunServerNoSystemd(t *testing.T) {
|
||||
th := SetupServerTest()
|
||||
defer th.TearDownServerTest()
|
||||
|
||||
// Temporarily remove any Systemd socket defined in the environment
|
||||
originalSocket := os.Getenv("NOTIFY_SOCKET")
|
||||
os.Unsetenv("NOTIFY_SOCKET")
|
||||
defer os.Setenv("NOTIFY_SOCKET", originalSocket)
|
||||
|
||||
err := runServer(th.configPath, th.disableConfigWatch, th.interruptChan)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
79
glide.lock
сгенерированный
79
glide.lock
сгенерированный
@@ -1,5 +1,5 @@
|
||||
hash: 5e8ab6acb5c3bb7dbedfc6e837cc529e72affea22384ec59e82f0e3c13379b6f
|
||||
updated: 2018-01-25T15:20:13.899302483-08:00
|
||||
hash: 6779beaa11fdb9c520471fb87c0a1a6ecc34a4c82610d942c44fba2f27a29936
|
||||
updated: 2018-02-15T18:28:32.209282461-08:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/log4go
|
||||
version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c
|
||||
@@ -13,7 +13,7 @@ imports:
|
||||
- name: github.com/corpix/uarand
|
||||
version: 2b8494104d86337cdd41d0a49cbed8e4583c0ab4
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: ecdeabc65495df2dec95d7c4a4c3e021903035e5
|
||||
version: 87df7c60d5820d0f8ae11afede5aa52325c09717
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/dgryski/dgoogauth
|
||||
@@ -29,11 +29,11 @@ imports:
|
||||
- name: github.com/fsnotify/fsnotify
|
||||
version: c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9
|
||||
- name: github.com/go-ini/ini
|
||||
version: 32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a
|
||||
version: 32e4be5f41bb918afb6e37c07426e2ddbcb6647e
|
||||
- name: github.com/go-ldap/ldap
|
||||
version: bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9
|
||||
- name: github.com/go-redis/redis
|
||||
version: 4021ace05686f632ff17fd824bbed229fc474cf8
|
||||
version: 8b4fa6d443e35ca8b1c37be877285252430b06a3
|
||||
subpackages:
|
||||
- internal
|
||||
- internal/consistenthash
|
||||
@@ -48,7 +48,7 @@ imports:
|
||||
- raster
|
||||
- truetype
|
||||
- name: github.com/golang/protobuf
|
||||
version: 925541529c1fa6821df4e44ce2723319eb2be768
|
||||
version: bbd03ef6da3a115852eaf24c8a1c46aeb39aa175
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gorilla/context
|
||||
@@ -57,12 +57,14 @@ imports:
|
||||
version: 90663712d74cb411cbef281bc1e08c19d1a76145
|
||||
- name: github.com/gorilla/mux
|
||||
version: 53c1911da2b537f792e7cafcb446b05ffe33b996
|
||||
- name: github.com/gorilla/schema
|
||||
version: afe77393c53b66afe9212810d9b2013859d04ae6
|
||||
- name: github.com/gorilla/websocket
|
||||
version: 91f589db023d66e4aba7112d44cc0d2fb091c553
|
||||
version: 4ac909741dfa57448bfadfdbca0cf7eeaa68f0e2
|
||||
- name: github.com/hashicorp/errwrap
|
||||
version: 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
||||
- name: github.com/hashicorp/go-immutable-radix
|
||||
version: 59b67882ec612f43b9d4c4fd97cebd507be4b3ee
|
||||
version: 7f3cd4390caab3250a57f30efdb2a65dd7649ecf
|
||||
- name: github.com/hashicorp/go-msgpack
|
||||
version: fa3f63826f7c23912c15263591e65d54d080b458
|
||||
subpackages:
|
||||
@@ -70,9 +72,9 @@ imports:
|
||||
- name: github.com/hashicorp/go-multierror
|
||||
version: b7773ae218740a7be65057fc60b366a49b538a44
|
||||
- name: github.com/hashicorp/go-sockaddr
|
||||
version: 9b4c5fa5b10a683339a270d664474b9f4aee62fc
|
||||
version: 7165ee14aff120ee3642aa2bcf2dea8eebef29c3
|
||||
- name: github.com/hashicorp/golang-lru
|
||||
version: 0a025b7e63adc15a622f29b0b2c4c3848243bbf6
|
||||
version: 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
|
||||
subpackages:
|
||||
- simplelru
|
||||
- name: github.com/hashicorp/hcl
|
||||
@@ -88,7 +90,7 @@ imports:
|
||||
- json/scanner
|
||||
- json/token
|
||||
- name: github.com/hashicorp/memberlist
|
||||
version: 3d8438da9589e7b608a83ffac1ef8211486bcb7c
|
||||
version: 2288bf30e9c8d7b5f6549bf62e07120d72fd4b6c
|
||||
- name: github.com/icrowley/fake
|
||||
version: 4178557ae428460c3780a381c824a1f3aceb6325
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
@@ -96,11 +98,11 @@ imports:
|
||||
- name: github.com/jehiah/go-strftime
|
||||
version: 834e15c05a45371503440cc195bbd05c9a0968d9
|
||||
- name: github.com/lib/pq
|
||||
version: 19c8e9ad00952ce0c64489b60e8df88bb16dd514
|
||||
version: 88edab0803230a3898347e77b474f8c1820a1f20
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/magiconair/properties
|
||||
version: 49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934
|
||||
version: c3beff4c2358b44d0493c7dda585e7db7ff28ae6
|
||||
- name: github.com/mailru/easyjson
|
||||
version: 32fa128f234d041f196a9f3e0fea5ac9772c08e1
|
||||
subpackages:
|
||||
@@ -124,7 +126,7 @@ imports:
|
||||
- name: github.com/miekg/dns
|
||||
version: 5364553f1ee9cddc7ac8b62dce148309c386695b
|
||||
- name: github.com/minio/minio-go
|
||||
version: 14f1d472d115bac5ca4804094aa87484a72ced61
|
||||
version: 706c81d3ee2a18cdd8239faf544de8a066e7e261
|
||||
subpackages:
|
||||
- pkg/credentials
|
||||
- pkg/encrypt
|
||||
@@ -135,7 +137,7 @@ imports:
|
||||
- name: github.com/mitchellh/go-homedir
|
||||
version: b8bc1bf767474819792c23f32d8286a45736f1c6
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: b4575eea38cca1123ec2dc90c26529b5c5acfcff
|
||||
version: a4e142e9c047c904fa2f1e144d9a84e6133024bc
|
||||
- name: github.com/mssola/user_agent
|
||||
version: 5243daae23628aeae9b6268541406bd5e95d5964
|
||||
- name: github.com/nicksnyder/go-i18n
|
||||
@@ -148,7 +150,7 @@ imports:
|
||||
- name: github.com/NYTimes/gziphandler
|
||||
version: 289a3b81f5aedc99f8d6eb0f67827c142f1310d8
|
||||
- name: github.com/olivere/elastic
|
||||
version: c51e74f9bcab8906a2f6cf5660dac396ba51b3d6
|
||||
version: e852184f51320ab81f9401428bec78e9ffe1355a
|
||||
subpackages:
|
||||
- config
|
||||
- uritemplates
|
||||
@@ -163,7 +165,7 @@ imports:
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/prometheus/client_golang
|
||||
version: 06bc6e01f4baf4ee783ffcd23abfcb0b0f9dfada
|
||||
version: fcc130e101e76c5d303513d0e28f4b6d732845c7
|
||||
subpackages:
|
||||
- prometheus
|
||||
- prometheus/promhttp
|
||||
@@ -178,13 +180,11 @@ imports:
|
||||
- internal/bitbucket.org/ww/goautoneg
|
||||
- model
|
||||
- name: github.com/prometheus/procfs
|
||||
version: cb4147076ac75738c9a7d279075a253c0cc5acbd
|
||||
version: 282c8707aa210456a825798969cc27edda34992a
|
||||
subpackages:
|
||||
- internal/util
|
||||
- nfs
|
||||
- xfs
|
||||
- name: github.com/rsc/letsencrypt
|
||||
version: 33926faef6d434b854ea994228f11d0185faa0c1
|
||||
- name: github.com/rwcarlsen/goexif
|
||||
version: 17202558c8d9c3fd047859f1a5e73fd9ae709187
|
||||
subpackages:
|
||||
@@ -197,23 +197,23 @@ imports:
|
||||
- name: github.com/segmentio/backo-go
|
||||
version: 204274ad699c0983a70203a566887f17a717fef4
|
||||
- name: github.com/spf13/afero
|
||||
version: bb8f1927f2a9d3ab41c9340aa034f6b803f4359c
|
||||
version: bbf41cb36dffe15dff5bf7e18c447801e7ffe163
|
||||
subpackages:
|
||||
- mem
|
||||
- name: github.com/spf13/cast
|
||||
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
|
||||
version: 8965335b8c7107321228e3e3702cab9832751bac
|
||||
- name: github.com/spf13/cobra
|
||||
version: f91529fc609202eededff4de2dc0ba2f662240a3
|
||||
version: be77323fc05148ef091e83b3866c0d47c8e74a8b
|
||||
- name: github.com/spf13/jwalterweatherman
|
||||
version: 7c0cea34c8ece3fbeb2b27ab9b59511d360fb394
|
||||
- name: github.com/spf13/pflag
|
||||
version: 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
|
||||
version: 6a877ebacf28c5fc79846f4fcd380a5d9872b997
|
||||
- name: github.com/spf13/viper
|
||||
version: aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5
|
||||
- name: github.com/stretchr/objx
|
||||
version: 477a77ecc69700c7cdeb1fa9e129548e1c1c393c
|
||||
version: 8a3f7159479fbc75b30357fbc48f380b7320f08e
|
||||
- name: github.com/stretchr/testify
|
||||
version: b91bfb9ebec76498946beb6af7c0230c7cc7ba6c
|
||||
version: 12b6f73e6084dad08a7c6e575284b177ecafbc71
|
||||
subpackages:
|
||||
- assert
|
||||
- mock
|
||||
@@ -221,20 +221,17 @@ imports:
|
||||
- suite
|
||||
- name: github.com/tylerb/graceful
|
||||
version: d72b0151351a13d0421b763b88f791469c4f5dc7
|
||||
- name: github.com/xenolf/lego
|
||||
version: 6bddbfd17a6e1ab782617eeab2f2007c6550b160
|
||||
subpackages:
|
||||
- acme
|
||||
- name: github.com/xtgo/uuid
|
||||
version: a0b114877d4caeffbd7f87e3757c17fce570fea7
|
||||
- name: golang.org/x/crypto
|
||||
version: 3d37316aaa6bd9929127ac9a527abf408178ea7b
|
||||
version: 650f4a345ab4e5b245a3034b110ebc7299e68186
|
||||
subpackages:
|
||||
- acme
|
||||
- acme/autocert
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- ed25519
|
||||
- ed25519/internal/edwards25519
|
||||
- ocsp
|
||||
- name: golang.org/x/image
|
||||
version: 12117c17ca67ffa1ce22e9409f3b0b0a93ac08c7
|
||||
subpackages:
|
||||
@@ -244,10 +241,9 @@ imports:
|
||||
- tiff
|
||||
- tiff/lzw
|
||||
- name: golang.org/x/net
|
||||
version: 0ed95abb35c445290478a5348a7b38bb154135fd
|
||||
version: dc948dff8834a7fe1ca525f8d04e261c2b56e70d
|
||||
subpackages:
|
||||
- bpf
|
||||
- context
|
||||
- html
|
||||
- html/atom
|
||||
- idna
|
||||
@@ -257,20 +253,16 @@ imports:
|
||||
- ipv6
|
||||
- lex/httplex
|
||||
- name: golang.org/x/sys
|
||||
version: 03467258950d845cd1877eab69461b98e8c09219
|
||||
version: 37707fdb30a5b38865cfb95e5aab41707daec7fd
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: e19ae1496984b1c655b8044a65c0300a3c878dd3
|
||||
version: 4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1
|
||||
subpackages:
|
||||
- secure/bidirule
|
||||
- transform
|
||||
- unicode/bidi
|
||||
- unicode/norm
|
||||
- name: golang.org/x/time
|
||||
version: 6dc17368e09b0e8634d71cac8168d853e869a0c7
|
||||
subpackages:
|
||||
- rate
|
||||
- name: google.golang.org/appengine
|
||||
version: 5bee14b453b4c71be47ec1781b0fa61c2ea182db
|
||||
subpackages:
|
||||
@@ -282,12 +274,7 @@ imports:
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: 41f3572897373c5538c50a2402db15db079fa4fd
|
||||
- name: gopkg.in/olivere/elastic.v5
|
||||
version: c51e74f9bcab8906a2f6cf5660dac396ba51b3d6
|
||||
- name: gopkg.in/square/go-jose.v1
|
||||
version: aa2e30fdd1fe9dd3394119af66451ae790d50e0d
|
||||
subpackages:
|
||||
- cipher
|
||||
- json
|
||||
version: 9f4560b20fb3bd4bb855fada3e6feea59b26ce66
|
||||
- name: gopkg.in/throttled/throttled.v2
|
||||
version: c4642cff38719000a875f10166ecb9599b002f96
|
||||
subpackages:
|
||||
|
||||
10
glide.yaml
10
glide.yaml
@@ -14,7 +14,7 @@ import:
|
||||
- package: github.com/go-ldap/ldap
|
||||
version: v2.5.1
|
||||
- package: github.com/go-redis/redis
|
||||
version: v6.8.2
|
||||
version: v6.8.3
|
||||
- package: github.com/go-sql-driver/mysql
|
||||
- package: github.com/golang/freetype
|
||||
- package: github.com/gorilla/handlers
|
||||
@@ -31,7 +31,7 @@ import:
|
||||
subpackages:
|
||||
- qr
|
||||
- package: github.com/minio/minio-go
|
||||
version: 4.0.6
|
||||
version: 4.0.7
|
||||
subpackages:
|
||||
- pkg/credentials
|
||||
- package: github.com/mssola/user_agent
|
||||
@@ -43,8 +43,6 @@ import:
|
||||
version: v1.1
|
||||
- package: github.com/pkg/errors
|
||||
version: v0.8.0
|
||||
- package: github.com/rsc/letsencrypt
|
||||
version: v0.0.1
|
||||
- package: github.com/rwcarlsen/goexif
|
||||
subpackages:
|
||||
- exif
|
||||
@@ -53,7 +51,7 @@ import:
|
||||
- package: github.com/spf13/cobra
|
||||
- package: github.com/spf13/viper
|
||||
- package: github.com/stretchr/testify
|
||||
version: v1.2.0
|
||||
version: v1.2.1
|
||||
subpackages:
|
||||
- assert
|
||||
- mock
|
||||
@@ -73,7 +71,7 @@ import:
|
||||
- package: gopkg.in/gomail.v2
|
||||
version: 2.0.0
|
||||
- package: gopkg.in/olivere/elastic.v5
|
||||
version: v6.1.4
|
||||
version: v6.1.7
|
||||
- package: gopkg.in/throttled/throttled.v2
|
||||
version: v2.1.0
|
||||
subpackages:
|
||||
|
||||
@@ -3074,6 +3074,10 @@
|
||||
"id": "api.webhook.incoming.debug.error",
|
||||
"translation": "Could not read payload of incoming webhook."
|
||||
},
|
||||
{
|
||||
"id": "api.webhook.incoming.error",
|
||||
"translation": "Could not decode the multipart payload of incoming webhook."
|
||||
},
|
||||
{
|
||||
"id": "api.webhook.init.debug",
|
||||
"translation": "Initializing webhook API routes"
|
||||
|
||||
@@ -2100,7 +2100,7 @@ func (ss *ServiceSettings) isValid() *AppError {
|
||||
}
|
||||
|
||||
switch *ss.ImageProxyType {
|
||||
case "", "willnorris/imageproxy":
|
||||
case "":
|
||||
case "atmos/camo":
|
||||
if *ss.ImageProxyOptions == "" {
|
||||
return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_options.app_error", nil, "", http.StatusBadRequest)
|
||||
|
||||
@@ -204,3 +204,12 @@ func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *A
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func (o *IncomingWebhookRequest) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s *SqlSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiN
|
||||
}
|
||||
|
||||
for _, reaction := range reactions {
|
||||
if _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_QUERY,
|
||||
if _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY,
|
||||
map[string]interface{}{"PostId": reaction.PostId, "UpdateAt": model.GetMillis()}); err != nil {
|
||||
l4g.Warn(utils.T("store.sql_reaction.delete_all_with_emoji_name.update_post.warn"), reaction.PostId, err.Error())
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func saveReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Re
|
||||
return err
|
||||
}
|
||||
|
||||
return updatePostForReactions(transaction, reaction.PostId)
|
||||
return updatePostForReactionsOnInsert(transaction, reaction.PostId)
|
||||
}
|
||||
|
||||
func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error {
|
||||
@@ -189,12 +189,12 @@ func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.
|
||||
return err
|
||||
}
|
||||
|
||||
return updatePostForReactions(transaction, reaction.PostId)
|
||||
return updatePostForReactionsOnDelete(transaction, reaction.PostId)
|
||||
}
|
||||
|
||||
const (
|
||||
// Set HasReactions = true if and only if the post has reactions, update UpdateAt only if HasReactions changes
|
||||
UPDATE_POST_HAS_REACTIONS_QUERY = `UPDATE
|
||||
UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY = `UPDATE
|
||||
Posts
|
||||
SET
|
||||
UpdateAt = (CASE
|
||||
@@ -206,8 +206,22 @@ const (
|
||||
Id = :PostId`
|
||||
)
|
||||
|
||||
func updatePostForReactions(transaction *gorp.Transaction, postId string) error {
|
||||
_, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
|
||||
func updatePostForReactionsOnDelete(transaction *gorp.Transaction, postId string) error {
|
||||
_, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func updatePostForReactionsOnInsert(transaction *gorp.Transaction, postId string) error {
|
||||
_, err := transaction.Exec(
|
||||
`UPDATE
|
||||
Posts
|
||||
SET
|
||||
HasReactions = True,
|
||||
UpdateAt = :UpdateAt
|
||||
WHERE
|
||||
Id = :PostId AND HasReactions = False`,
|
||||
map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -157,19 +157,18 @@ func sendMail(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string
|
||||
}
|
||||
|
||||
for _, fileInfo := range attachments {
|
||||
bytes, err := fileBackend.ReadFile(fileInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Attach(fileInfo.Name, gomail.SetCopyFunc(func(writer io.Writer) error {
|
||||
bytes, err := fileBackend.ReadFile(fileInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := writer.Write(bytes); err != nil {
|
||||
return model.NewAppError("SendMail", "utils.mail.sendMail.attachments.write_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
conn, err1 := connectToSMTPServer(config)
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
|
||||
"net/mail"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -94,18 +96,28 @@ func TestSendMailUsingConfigAdvanced(t *testing.T) {
|
||||
//Delete all the messages before check the sample email
|
||||
DeleteMailBox(smtpTo)
|
||||
|
||||
// create a file that will be attached to the email
|
||||
fileBackend, err := NewFileBackend(&cfg.FileSettings, true)
|
||||
assert.Nil(t, err)
|
||||
fileContents := []byte("hello world")
|
||||
fileName := "file.txt"
|
||||
assert.Nil(t, fileBackend.WriteFile(fileContents, fileName))
|
||||
defer fileBackend.RemoveFile(fileName)
|
||||
|
||||
attachments := make([]*model.FileInfo, 1)
|
||||
// create two files with the same name that will both be attached to the email
|
||||
fileName := "file.txt"
|
||||
filePath1 := fmt.Sprintf("test1/%s", fileName)
|
||||
filePath2 := fmt.Sprintf("test2/%s", fileName)
|
||||
fileContents1 := []byte("hello world")
|
||||
fileContents2 := []byte("foo bar")
|
||||
assert.Nil(t, fileBackend.WriteFile(fileContents1, filePath1))
|
||||
assert.Nil(t, fileBackend.WriteFile(fileContents2, filePath2))
|
||||
defer fileBackend.RemoveFile(filePath1)
|
||||
defer fileBackend.RemoveFile(filePath2)
|
||||
|
||||
attachments := make([]*model.FileInfo, 2)
|
||||
attachments[0] = &model.FileInfo{
|
||||
Name: fileName,
|
||||
Path: fileName,
|
||||
Path: filePath1,
|
||||
}
|
||||
attachments[1] = &model.FileInfo{
|
||||
Name: fileName,
|
||||
Path: filePath2,
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
@@ -145,10 +157,19 @@ func TestSendMailUsingConfigAdvanced(t *testing.T) {
|
||||
// check that the custom mime headers came through - header case seems to get mutated
|
||||
assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0])
|
||||
|
||||
// ensure that the attachment was successfully sent
|
||||
assert.Len(t, resultsEmail.Attachments, 1)
|
||||
// ensure that the attachments were successfully sent
|
||||
assert.Len(t, resultsEmail.Attachments, 2)
|
||||
assert.Equal(t, fileName, resultsEmail.Attachments[0].Filename)
|
||||
assert.Equal(t, fileContents, resultsEmail.Attachments[0].Bytes)
|
||||
assert.Equal(t, fileName, resultsEmail.Attachments[1].Filename)
|
||||
attachment1 := string(resultsEmail.Attachments[0].Bytes)
|
||||
attachment2 := string(resultsEmail.Attachments[1].Bytes)
|
||||
if attachment1 == string(fileContents1) {
|
||||
assert.Equal(t, attachment2, string(fileContents2))
|
||||
} else if attachment1 == string(fileContents2) {
|
||||
assert.Equal(t, attachment2, string(fileContents1))
|
||||
} else {
|
||||
assert.Fail(t, "Unrecognized attachment contents")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
187
vendor/github.com/davecgh/go-spew/spew/bypass.go
сгенерированный
поставляемый
187
vendor/github.com/davecgh/go-spew/spew/bypass.go
сгенерированный
поставляемый
@@ -16,7 +16,9 @@
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
@@ -34,80 +36,49 @@ const (
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = ptrSize
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = ptrSize * 2
|
||||
type flag uintptr
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = flagKindWidth - 1
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
@@ -119,34 +90,56 @@ func init() {
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
сгенерированный
поставляемый
2
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
сгенерированный
поставляемый
@@ -16,7 +16,7 @@
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/dump_test.go
сгенерированный
поставляемый
2
vendor/github.com/davecgh/go-spew/spew/dump_test.go
сгенерированный
поставляемый
@@ -768,7 +768,7 @@ func addUintptrDumpTests() {
|
||||
|
||||
func addUnsafePointerDumpTests() {
|
||||
// Null pointer.
|
||||
v := unsafe.Pointer(uintptr(0))
|
||||
v := unsafe.Pointer(nil)
|
||||
nv := (*unsafe.Pointer)(nil)
|
||||
pv := &v
|
||||
vAddr := fmt.Sprintf("%p", pv)
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/format_test.go
сгенерированный
поставляемый
2
vendor/github.com/davecgh/go-spew/spew/format_test.go
сгенерированный
поставляемый
@@ -1083,7 +1083,7 @@ func addUintptrFormatterTests() {
|
||||
|
||||
func addUnsafePointerFormatterTests() {
|
||||
// Null pointer.
|
||||
v := unsafe.Pointer(uintptr(0))
|
||||
v := unsafe.Pointer(nil)
|
||||
nv := (*unsafe.Pointer)(nil)
|
||||
pv := &v
|
||||
vAddr := fmt.Sprintf("%p", pv)
|
||||
|
||||
11
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
сгенерированный
поставляемый
11
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
сгенерированный
поставляемый
@@ -16,7 +16,7 @@
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
/*
|
||||
This test file is part of the spew package rather than than the spew_test
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
|
||||
@@ -38,13 +37,13 @@ import (
|
||||
// fallback code which punts to the standard fmt library for new types that
|
||||
// might get added to the language.
|
||||
func changeKind(v *reflect.Value, readOnly bool) {
|
||||
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
|
||||
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
|
||||
flags := flagField(v)
|
||||
if readOnly {
|
||||
*rvf |= flagRO
|
||||
*flags |= flagRO
|
||||
} else {
|
||||
*rvf &= ^uintptr(flagRO)
|
||||
*flags &^= flagRO
|
||||
}
|
||||
*flags |= flagKindMask
|
||||
}
|
||||
|
||||
// TestAddedReflectValue tests functionaly of the dump and formatter code which
|
||||
|
||||
93
vendor/github.com/go-ini/ini/file_test.go
сгенерированный
поставляемый
93
vendor/github.com/go-ini/ini/file_test.go
сгенерированный
поставляемый
@@ -16,6 +16,7 @@ package ini_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
@@ -253,93 +254,15 @@ func TestFile_WriteTo(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
_, err = f.WriteTo(&buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(buf.String(), ShouldEqual, `; Package name
|
||||
NAME = ini
|
||||
; Package version
|
||||
VERSION = v1
|
||||
; Package import path
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
; Information about package author
|
||||
# Bio can be written in multiple lines.
|
||||
[author]
|
||||
; This is author name
|
||||
NAME = Unknwon
|
||||
E-MAIL = u@gogs.io
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
# Succeeding comment
|
||||
BIO = """Gopher.
|
||||
Coding addict.
|
||||
Good man.
|
||||
"""
|
||||
golden := "testdata/TestFile_WriteTo.golden"
|
||||
if *update {
|
||||
ioutil.WriteFile(golden, buf.Bytes(), 0644)
|
||||
}
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
UNUSED_KEY = should be deleted
|
||||
|
||||
[features]
|
||||
- = Support read/write comments of keys and sections
|
||||
- = Support auto-increment of key names
|
||||
- = Support load multiple files to overwrite key values
|
||||
|
||||
[types]
|
||||
STRING = str
|
||||
BOOL = true
|
||||
BOOL_FALSE = false
|
||||
FLOAT64 = 1.25
|
||||
INT = 10
|
||||
TIME = 2015-01-01T20:17:05Z
|
||||
DURATION = 2h45m
|
||||
UINT = 3
|
||||
|
||||
[array]
|
||||
STRINGS = en, zh, de
|
||||
FLOAT64S = 1.1, 2.2, 3.3
|
||||
INTS = 1, 2, 3
|
||||
UINTS = 1, 2, 3
|
||||
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
|
||||
|
||||
[note]
|
||||
empty_lines = next line is empty
|
||||
boolean_key
|
||||
more = notes
|
||||
|
||||
; Comment before the section
|
||||
; This is a comment for the section too
|
||||
[comments]
|
||||
; Comment before key
|
||||
key = value
|
||||
; This is a comment for key2
|
||||
key2 = value2
|
||||
key3 = "one", "two", "three"
|
||||
|
||||
[string escapes]
|
||||
key1 = value1, value2, value3
|
||||
key2 = value1\, value2
|
||||
key3 = val\ue1, value2
|
||||
key4 = value1\\, value\\\\2
|
||||
key5 = value1\,, value2
|
||||
key6 = aaa bbb\ and\ space ccc
|
||||
|
||||
[advance]
|
||||
value with quotes = some value
|
||||
value quote2 again = some value
|
||||
includes comment sign = `+"`"+"my#password"+"`"+`
|
||||
includes comment sign2 = `+"`"+"my;password"+"`"+`
|
||||
true = 2+3=5
|
||||
`+"`"+`1+1=2`+"`"+` = true
|
||||
`+"`"+`6+1=7`+"`"+` = true
|
||||
"""`+"`"+`5+5`+"`"+`""" = 10
|
||||
`+"`"+`"6+6"`+"`"+` = 12
|
||||
`+"`"+`7-2=4`+"`"+` = false
|
||||
ADDRESS = """404 road,
|
||||
NotFound, State, 50000"""
|
||||
two_lines = how about continuation lines?
|
||||
lots_of_lines = 1 2 3 4
|
||||
|
||||
`)
|
||||
expected, err := ioutil.ReadFile(golden)
|
||||
So(err, ShouldBeNil)
|
||||
So(buf.String(), ShouldEqual, string(expected))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/go-ini/ini/ini.go
сгенерированный
поставляемый
2
vendor/github.com/go-ini/ini/ini.go
сгенерированный
поставляемый
@@ -32,7 +32,7 @@ const (
|
||||
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
_DEPTH_VALUES = 99
|
||||
_VERSION = "1.32.0"
|
||||
_VERSION = "1.32.1"
|
||||
)
|
||||
|
||||
// Version returns current package version literal.
|
||||
|
||||
3
vendor/github.com/go-ini/ini/ini_test.go
сгенерированный
поставляемый
3
vendor/github.com/go-ini/ini/ini_test.go
сгенерированный
поставляемый
@@ -16,6 +16,7 @@ package ini_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
@@ -47,6 +48,8 @@ const (
|
||||
_NOT_FOUND_CONF = "testdata/404.ini"
|
||||
)
|
||||
|
||||
var update = flag.Bool("update", false, "Update .golden files")
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
Convey("Load from good data sources", t, func() {
|
||||
f, err := ini.Load([]byte(`
|
||||
|
||||
86
vendor/github.com/go-ini/ini/testdata/TestFile_WriteTo.golden
сгенерированный
поставляемый
Обычный файл
86
vendor/github.com/go-ini/ini/testdata/TestFile_WriteTo.golden
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,86 @@
|
||||
; Package name
|
||||
NAME = ini
|
||||
; Package version
|
||||
VERSION = v1
|
||||
; Package import path
|
||||
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
|
||||
|
||||
; Information about package author
|
||||
# Bio can be written in multiple lines.
|
||||
[author]
|
||||
; This is author name
|
||||
NAME = Unknwon
|
||||
E-MAIL = u@gogs.io
|
||||
GITHUB = https://github.com/%(NAME)s
|
||||
# Succeeding comment
|
||||
BIO = """Gopher.
|
||||
Coding addict.
|
||||
Good man.
|
||||
"""
|
||||
|
||||
[package]
|
||||
CLONE_URL = https://%(IMPORT_PATH)s
|
||||
|
||||
[package.sub]
|
||||
UNUSED_KEY = should be deleted
|
||||
|
||||
[features]
|
||||
- = Support read/write comments of keys and sections
|
||||
- = Support auto-increment of key names
|
||||
- = Support load multiple files to overwrite key values
|
||||
|
||||
[types]
|
||||
STRING = str
|
||||
BOOL = true
|
||||
BOOL_FALSE = false
|
||||
FLOAT64 = 1.25
|
||||
INT = 10
|
||||
TIME = 2015-01-01T20:17:05Z
|
||||
DURATION = 2h45m
|
||||
UINT = 3
|
||||
|
||||
[array]
|
||||
STRINGS = en, zh, de
|
||||
FLOAT64S = 1.1, 2.2, 3.3
|
||||
INTS = 1, 2, 3
|
||||
UINTS = 1, 2, 3
|
||||
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
|
||||
|
||||
[note]
|
||||
empty_lines = next line is empty
|
||||
boolean_key
|
||||
more = notes
|
||||
|
||||
; Comment before the section
|
||||
; This is a comment for the section too
|
||||
[comments]
|
||||
; Comment before key
|
||||
key = value
|
||||
; This is a comment for key2
|
||||
key2 = value2
|
||||
key3 = "one", "two", "three"
|
||||
|
||||
[string escapes]
|
||||
key1 = value1, value2, value3
|
||||
key2 = value1\, value2
|
||||
key3 = val\ue1, value2
|
||||
key4 = value1\\, value\\\\2
|
||||
key5 = value1\,, value2
|
||||
key6 = aaa bbb\ and\ space ccc
|
||||
|
||||
[advance]
|
||||
value with quotes = some value
|
||||
value quote2 again = some value
|
||||
includes comment sign = `my#password`
|
||||
includes comment sign2 = `my;password`
|
||||
true = 2+3=5
|
||||
`1+1=2` = true
|
||||
`6+1=7` = true
|
||||
"""`5+5`""" = 10
|
||||
`"6+6"` = 12
|
||||
`7-2=4` = false
|
||||
ADDRESS = """404 road,
|
||||
NotFound, State, 50000"""
|
||||
two_lines = how about continuation lines?
|
||||
lots_of_lines = 1 2 3 4
|
||||
|
||||
42
vendor/github.com/go-redis/redis/cluster.go
сгенерированный
поставляемый
42
vendor/github.com/go-redis/redis/cluster.go
сгенерированный
поставляемый
@@ -445,6 +445,10 @@ type ClusterClient struct {
|
||||
cmdsInfoOnce internal.Once
|
||||
cmdsInfo map[string]*CommandInfo
|
||||
|
||||
process func(Cmder) error
|
||||
processPipeline func([]Cmder) error
|
||||
processTxPipeline func([]Cmder) error
|
||||
|
||||
// Reports whether slots reloading is in progress.
|
||||
reloading uint32
|
||||
}
|
||||
@@ -458,7 +462,12 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
|
||||
opt: opt,
|
||||
nodes: newClusterNodes(opt),
|
||||
}
|
||||
c.setProcessor(c.Process)
|
||||
|
||||
c.process = c.defaultProcess
|
||||
c.processPipeline = c.defaultProcessPipeline
|
||||
c.processTxPipeline = c.defaultProcessTxPipeline
|
||||
|
||||
c.cmdable.setProcessor(c.Process)
|
||||
|
||||
// Add initial nodes.
|
||||
for _, addr := range opt.Addrs {
|
||||
@@ -628,7 +637,20 @@ func (c *ClusterClient) Close() error {
|
||||
return c.nodes.Close()
|
||||
}
|
||||
|
||||
func (c *ClusterClient) WrapProcess(
|
||||
fn func(oldProcess func(Cmder) error) func(Cmder) error,
|
||||
) {
|
||||
c.process = fn(c.process)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) Process(cmd Cmder) error {
|
||||
if c.process != nil {
|
||||
return c.process(cmd)
|
||||
}
|
||||
return c.defaultProcess(cmd)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) defaultProcess(cmd Cmder) error {
|
||||
state, err := c.state()
|
||||
if err != nil {
|
||||
cmd.setErr(err)
|
||||
@@ -910,9 +932,9 @@ func (c *ClusterClient) reaper(idleCheckFrequency time.Duration) {
|
||||
|
||||
func (c *ClusterClient) Pipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExec,
|
||||
exec: c.processPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -920,7 +942,13 @@ func (c *ClusterClient) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
return c.Pipeline().Pipelined(fn)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) pipelineExec(cmds []Cmder) error {
|
||||
func (c *ClusterClient) WrapProcessPipeline(
|
||||
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
|
||||
) {
|
||||
c.processPipeline = fn(c.processPipeline)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) defaultProcessPipeline(cmds []Cmder) error {
|
||||
cmdsMap, err := c.mapCmdsByNode(cmds)
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
@@ -1064,9 +1092,9 @@ func (c *ClusterClient) checkMovedErr(
|
||||
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
||||
func (c *ClusterClient) TxPipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.txPipelineExec,
|
||||
exec: c.processTxPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -1074,7 +1102,7 @@ func (c *ClusterClient) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
return c.TxPipeline().Pipelined(fn)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) txPipelineExec(cmds []Cmder) error {
|
||||
func (c *ClusterClient) defaultProcessTxPipeline(cmds []Cmder) error {
|
||||
state, err := c.state()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
4
vendor/github.com/go-redis/redis/command.go
сгенерированный
поставляемый
4
vendor/github.com/go-redis/redis/command.go
сгенерированный
поставляемый
@@ -81,9 +81,9 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
|
||||
case "eval", "evalsha":
|
||||
if cmd.stringArg(2) != "0" {
|
||||
return 3
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
case "publish":
|
||||
return 1
|
||||
}
|
||||
|
||||
77
vendor/github.com/go-redis/redis/example_instrumentation_test.go
сгенерированный
поставляемый
77
vendor/github.com/go-redis/redis/example_instrumentation_test.go
сгенерированный
поставляемый
@@ -2,58 +2,47 @@ package redis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
func Example_instrumentation() {
|
||||
ring := redis.NewRing(&redis.RingOptions{
|
||||
Addrs: map[string]string{
|
||||
"shard1": ":6379",
|
||||
},
|
||||
cl := redis.NewClient(&redis.Options{
|
||||
Addr: ":6379",
|
||||
})
|
||||
ring.ForEachShard(func(client *redis.Client) error {
|
||||
wrapRedisProcess(client)
|
||||
return nil
|
||||
})
|
||||
|
||||
for {
|
||||
ring.Ping()
|
||||
}
|
||||
}
|
||||
|
||||
func wrapRedisProcess(client *redis.Client) {
|
||||
const precision = time.Microsecond
|
||||
var count, avgDur uint32
|
||||
|
||||
go func() {
|
||||
for range time.Tick(3 * time.Second) {
|
||||
n := atomic.LoadUint32(&count)
|
||||
dur := time.Duration(atomic.LoadUint32(&avgDur)) * precision
|
||||
fmt.Printf("%s: processed=%d avg_dur=%s\n", client, n, dur)
|
||||
}
|
||||
}()
|
||||
|
||||
client.WrapProcess(func(oldProcess func(redis.Cmder) error) func(redis.Cmder) error {
|
||||
cl.WrapProcess(func(old func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(cmd redis.Cmder) error {
|
||||
start := time.Now()
|
||||
err := oldProcess(cmd)
|
||||
dur := time.Since(start)
|
||||
|
||||
const decay = float64(1) / 100
|
||||
ms := float64(dur / precision)
|
||||
for {
|
||||
avg := atomic.LoadUint32(&avgDur)
|
||||
newAvg := uint32((1-decay)*float64(avg) + decay*ms)
|
||||
if atomic.CompareAndSwapUint32(&avgDur, avg, newAvg) {
|
||||
break
|
||||
}
|
||||
}
|
||||
atomic.AddUint32(&count, 1)
|
||||
|
||||
fmt.Printf("starting processing: <%s>\n", cmd)
|
||||
err := old(cmd)
|
||||
fmt.Printf("finished processing: <%s>\n", cmd)
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
cl.Ping()
|
||||
// Output: starting processing: <ping: >
|
||||
// finished processing: <ping: PONG>
|
||||
}
|
||||
|
||||
func Example_Pipeline_instrumentation() {
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: ":6379",
|
||||
})
|
||||
|
||||
client.WrapProcessPipeline(func(old func([]redis.Cmder) error) func([]redis.Cmder) error {
|
||||
return func(cmds []redis.Cmder) error {
|
||||
fmt.Printf("pipeline starting processing: %v\n", cmds)
|
||||
err := old(cmds)
|
||||
fmt.Printf("pipeline finished processing: %v\n", cmds)
|
||||
return err
|
||||
}
|
||||
})
|
||||
|
||||
client.Pipelined(func(pipe redis.Pipeliner) error {
|
||||
pipe.Ping()
|
||||
pipe.Ping()
|
||||
return nil
|
||||
})
|
||||
// Output: pipeline starting processing: [ping: ping: ]
|
||||
// pipeline finished processing: [ping: PONG ping: PONG]
|
||||
}
|
||||
|
||||
66
vendor/github.com/go-redis/redis/internal/proto/reader.go
сгенерированный
поставляемый
66
vendor/github.com/go-redis/redis/internal/proto/reader.go
сгенерированный
поставляемый
@@ -37,25 +37,25 @@ func (r *Reader) Reset(rd io.Reader) {
|
||||
r.src.Reset(rd)
|
||||
}
|
||||
|
||||
func (p *Reader) PeekBuffered() []byte {
|
||||
if n := p.src.Buffered(); n != 0 {
|
||||
b, _ := p.src.Peek(n)
|
||||
func (r *Reader) PeekBuffered() []byte {
|
||||
if n := r.src.Buffered(); n != 0 {
|
||||
b, _ := r.src.Peek(n)
|
||||
return b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Reader) ReadN(n int) ([]byte, error) {
|
||||
b, err := readN(p.src, p.buf, n)
|
||||
func (r *Reader) ReadN(n int) ([]byte, error) {
|
||||
b, err := readN(r.src, r.buf, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.buf = b
|
||||
r.buf = b
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (p *Reader) ReadLine() ([]byte, error) {
|
||||
line, isPrefix, err := p.src.ReadLine()
|
||||
func (r *Reader) ReadLine() ([]byte, error) {
|
||||
line, isPrefix, err := r.src.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,8 +71,8 @@ func (p *Reader) ReadLine() ([]byte, error) {
|
||||
return line, nil
|
||||
}
|
||||
|
||||
func (p *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
||||
line, err := p.ReadLine()
|
||||
func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,19 +85,19 @@ func (p *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
||||
case IntReply:
|
||||
return parseInt(line[1:], 10, 64)
|
||||
case StringReply:
|
||||
return p.readTmpBytesValue(line)
|
||||
return r.readTmpBytesValue(line)
|
||||
case ArrayReply:
|
||||
n, err := parseArrayLen(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m(p, n)
|
||||
return m(r, n)
|
||||
}
|
||||
return nil, fmt.Errorf("redis: can't parse %.100q", line)
|
||||
}
|
||||
|
||||
func (p *Reader) ReadIntReply() (int64, error) {
|
||||
line, err := p.ReadLine()
|
||||
func (r *Reader) ReadIntReply() (int64, error) {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -111,8 +111,8 @@ func (p *Reader) ReadIntReply() (int64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Reader) ReadTmpBytesReply() ([]byte, error) {
|
||||
line, err := p.ReadLine()
|
||||
func (r *Reader) ReadTmpBytesReply() ([]byte, error) {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func (p *Reader) ReadTmpBytesReply() ([]byte, error) {
|
||||
case ErrorReply:
|
||||
return nil, ParseErrorReply(line)
|
||||
case StringReply:
|
||||
return p.readTmpBytesValue(line)
|
||||
return r.readTmpBytesValue(line)
|
||||
case StatusReply:
|
||||
return parseStatusValue(line), nil
|
||||
default:
|
||||
@@ -138,24 +138,24 @@ func (r *Reader) ReadBytesReply() ([]byte, error) {
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
func (p *Reader) ReadStringReply() (string, error) {
|
||||
b, err := p.ReadTmpBytesReply()
|
||||
func (r *Reader) ReadStringReply() (string, error) {
|
||||
b, err := r.ReadTmpBytesReply()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (p *Reader) ReadFloatReply() (float64, error) {
|
||||
b, err := p.ReadTmpBytesReply()
|
||||
func (r *Reader) ReadFloatReply() (float64, error) {
|
||||
b, err := r.ReadTmpBytesReply()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return parseFloat(b, 64)
|
||||
}
|
||||
|
||||
func (p *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
|
||||
line, err := p.ReadLine()
|
||||
func (r *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -167,14 +167,14 @@ func (p *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m(p, n)
|
||||
return m(r, n)
|
||||
default:
|
||||
return nil, fmt.Errorf("redis: can't parse array reply: %.100q", line)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Reader) ReadArrayLen() (int64, error) {
|
||||
line, err := p.ReadLine()
|
||||
func (r *Reader) ReadArrayLen() (int64, error) {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -188,8 +188,8 @@ func (p *Reader) ReadArrayLen() (int64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Reader) ReadScanReply() ([]string, uint64, error) {
|
||||
n, err := p.ReadArrayLen()
|
||||
func (r *Reader) ReadScanReply() ([]string, uint64, error) {
|
||||
n, err := r.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -197,19 +197,19 @@ func (p *Reader) ReadScanReply() ([]string, uint64, error) {
|
||||
return nil, 0, fmt.Errorf("redis: got %d elements in scan reply, expected 2", n)
|
||||
}
|
||||
|
||||
cursor, err := p.ReadUint()
|
||||
cursor, err := r.ReadUint()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
n, err = p.ReadArrayLen()
|
||||
n, err = r.ReadArrayLen()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
keys := make([]string, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
key, err := p.ReadStringReply()
|
||||
key, err := r.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func (p *Reader) ReadScanReply() ([]string, uint64, error) {
|
||||
return keys, cursor, err
|
||||
}
|
||||
|
||||
func (p *Reader) readTmpBytesValue(line []byte) ([]byte, error) {
|
||||
func (r *Reader) readTmpBytesValue(line []byte) ([]byte, error) {
|
||||
if isNilReply(line) {
|
||||
return nil, internal.Nil
|
||||
}
|
||||
@@ -229,7 +229,7 @@ func (p *Reader) readTmpBytesValue(line []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := p.ReadN(replyLen + 2)
|
||||
b, err := r.ReadN(replyLen + 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
10
vendor/github.com/go-redis/redis/pubsub.go
сгенерированный
поставляемый
10
vendor/github.com/go-redis/redis/pubsub.go
сгенерированный
поставляемый
@@ -127,7 +127,7 @@ func (c *PubSub) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subscribes the client to the specified channels. It returns
|
||||
// Subscribe the client to the specified channels. It returns
|
||||
// empty subscription if there are no channels.
|
||||
func (c *PubSub) Subscribe(channels ...string) error {
|
||||
c.mu.Lock()
|
||||
@@ -137,7 +137,7 @@ func (c *PubSub) Subscribe(channels ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Subscribes the client to the given patterns. It returns
|
||||
// PSubscribe the client to the given patterns. It returns
|
||||
// empty subscription if there are no patterns.
|
||||
func (c *PubSub) PSubscribe(patterns ...string) error {
|
||||
c.mu.Lock()
|
||||
@@ -147,7 +147,7 @@ func (c *PubSub) PSubscribe(patterns ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unsubscribes the client from the given channels, or from all of
|
||||
// Unsubscribe the client from the given channels, or from all of
|
||||
// them if none is given.
|
||||
func (c *PubSub) Unsubscribe(channels ...string) error {
|
||||
c.mu.Lock()
|
||||
@@ -157,7 +157,7 @@ func (c *PubSub) Unsubscribe(channels ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unsubscribes the client from the given patterns, or from all of
|
||||
// PUnsubscribe the client from the given patterns, or from all of
|
||||
// them if none is given.
|
||||
func (c *PubSub) PUnsubscribe(patterns ...string) error {
|
||||
c.mu.Lock()
|
||||
@@ -196,7 +196,7 @@ func (c *PubSub) Ping(payload ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Message received after a successful subscription to channel.
|
||||
// Subscription received after a successful subscription to channel.
|
||||
type Subscription struct {
|
||||
// Can be "subscribe", "unsubscribe", "psubscribe" or "punsubscribe".
|
||||
Kind string
|
||||
|
||||
110
vendor/github.com/go-redis/redis/redis.go
сгенерированный
поставляемый
110
vendor/github.com/go-redis/redis/redis.go
сгенерированный
поставляемый
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/go-redis/redis/internal/proto"
|
||||
)
|
||||
|
||||
// Redis nil reply returned when key does not exist.
|
||||
// Nil reply redis returned when key does not exist.
|
||||
const Nil = internal.Nil
|
||||
|
||||
func init() {
|
||||
@@ -22,6 +22,12 @@ func SetLogger(logger *log.Logger) {
|
||||
internal.Logger = logger
|
||||
}
|
||||
|
||||
func (c *baseClient) init() {
|
||||
c.process = c.defaultProcess
|
||||
c.processPipeline = c.defaultProcessPipeline
|
||||
c.processTxPipeline = c.defaultProcessTxPipeline
|
||||
}
|
||||
|
||||
func (c *baseClient) String() string {
|
||||
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
|
||||
}
|
||||
@@ -85,7 +91,8 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
|
||||
connPool: pool.NewSingleConnPool(cn),
|
||||
},
|
||||
}
|
||||
conn.setProcessor(conn.Process)
|
||||
conn.baseClient.init()
|
||||
conn.statefulCmdable.setProcessor(conn.Process)
|
||||
|
||||
_, err := conn.Pipelined(func(pipe Pipeliner) error {
|
||||
if c.opt.Password != "" {
|
||||
@@ -117,14 +124,11 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
|
||||
// an input and returns the new wrapper process func. createWrapper should
|
||||
// use call the old process func within the new process func.
|
||||
func (c *baseClient) WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error) {
|
||||
c.process = fn(c.defaultProcess)
|
||||
c.process = fn(c.process)
|
||||
}
|
||||
|
||||
func (c *baseClient) Process(cmd Cmder) error {
|
||||
if c.process != nil {
|
||||
return c.process(cmd)
|
||||
}
|
||||
return c.defaultProcess(cmd)
|
||||
return c.process(cmd)
|
||||
}
|
||||
|
||||
func (c *baseClient) defaultProcess(cmd Cmder) error {
|
||||
@@ -172,9 +176,9 @@ func (c *baseClient) retryBackoff(attempt int) time.Duration {
|
||||
func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
|
||||
if timeout := cmd.readTimeout(); timeout != nil {
|
||||
return *timeout
|
||||
} else {
|
||||
return c.opt.ReadTimeout
|
||||
}
|
||||
|
||||
return c.opt.ReadTimeout
|
||||
}
|
||||
|
||||
// Close closes the client, releasing any open resources.
|
||||
@@ -198,35 +202,48 @@ func (c *baseClient) getAddr() string {
|
||||
return c.opt.Addr
|
||||
}
|
||||
|
||||
func (c *baseClient) WrapProcessPipeline(
|
||||
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
|
||||
) {
|
||||
c.processPipeline = fn(c.processPipeline)
|
||||
c.processTxPipeline = fn(c.processTxPipeline)
|
||||
}
|
||||
|
||||
func (c *baseClient) defaultProcessPipeline(cmds []Cmder) error {
|
||||
return c.generalProcessPipeline(cmds, c.pipelineProcessCmds)
|
||||
}
|
||||
|
||||
func (c *baseClient) defaultProcessTxPipeline(cmds []Cmder) error {
|
||||
return c.generalProcessPipeline(cmds, c.txPipelineProcessCmds)
|
||||
}
|
||||
|
||||
type pipelineProcessor func(*pool.Conn, []Cmder) (bool, error)
|
||||
|
||||
func (c *baseClient) pipelineExecer(p pipelineProcessor) pipelineExecer {
|
||||
return func(cmds []Cmder) error {
|
||||
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
time.Sleep(c.retryBackoff(attempt))
|
||||
}
|
||||
|
||||
cn, _, err := c.getConn()
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
return err
|
||||
}
|
||||
|
||||
canRetry, err := p(cn, cmds)
|
||||
|
||||
if err == nil || internal.IsRedisError(err) {
|
||||
_ = c.connPool.Put(cn)
|
||||
break
|
||||
}
|
||||
_ = c.connPool.Remove(cn)
|
||||
|
||||
if !canRetry || !internal.IsRetryableError(err, true) {
|
||||
break
|
||||
}
|
||||
func (c *baseClient) generalProcessPipeline(cmds []Cmder, p pipelineProcessor) error {
|
||||
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
time.Sleep(c.retryBackoff(attempt))
|
||||
}
|
||||
|
||||
cn, _, err := c.getConn()
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
return err
|
||||
}
|
||||
|
||||
canRetry, err := p(cn, cmds)
|
||||
|
||||
if err == nil || internal.IsRedisError(err) {
|
||||
_ = c.connPool.Put(cn)
|
||||
break
|
||||
}
|
||||
_ = c.connPool.Remove(cn)
|
||||
|
||||
if !canRetry || !internal.IsRetryableError(err, true) {
|
||||
break
|
||||
}
|
||||
return firstCmdsErr(cmds)
|
||||
}
|
||||
return firstCmdsErr(cmds)
|
||||
}
|
||||
|
||||
func (c *baseClient) pipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
|
||||
@@ -324,14 +341,15 @@ type Client struct {
|
||||
}
|
||||
|
||||
func newClient(opt *Options, pool pool.Pooler) *Client {
|
||||
client := Client{
|
||||
c := Client{
|
||||
baseClient: baseClient{
|
||||
opt: opt,
|
||||
connPool: pool,
|
||||
},
|
||||
}
|
||||
client.setProcessor(client.Process)
|
||||
return &client
|
||||
c.baseClient.init()
|
||||
c.cmdable.setProcessor(c.Process)
|
||||
return &c
|
||||
}
|
||||
|
||||
// NewClient returns a client to the Redis Server specified by Options.
|
||||
@@ -343,7 +361,7 @@ func NewClient(opt *Options) *Client {
|
||||
func (c *Client) copy() *Client {
|
||||
c2 := new(Client)
|
||||
*c2 = *c
|
||||
c2.setProcessor(c2.Process)
|
||||
c2.cmdable.setProcessor(c2.Process)
|
||||
return c2
|
||||
}
|
||||
|
||||
@@ -366,9 +384,9 @@ func (c *Client) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
|
||||
func (c *Client) Pipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExecer(c.pipelineProcessCmds),
|
||||
exec: c.processPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -379,9 +397,9 @@ func (c *Client) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
||||
func (c *Client) TxPipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExecer(c.txPipelineProcessCmds),
|
||||
exec: c.processTxPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -430,9 +448,9 @@ func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
|
||||
func (c *Conn) Pipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExecer(c.pipelineProcessCmds),
|
||||
exec: c.processPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -443,8 +461,8 @@ func (c *Conn) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
||||
func (c *Conn) TxPipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExecer(c.txPipelineProcessCmds),
|
||||
exec: c.processTxPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
5
vendor/github.com/go-redis/redis/redis_context.go
сгенерированный
поставляемый
5
vendor/github.com/go-redis/redis/redis_context.go
сгенерированный
поставляемый
@@ -12,7 +12,10 @@ type baseClient struct {
|
||||
connPool pool.Pooler
|
||||
opt *Options
|
||||
|
||||
process func(Cmder) error
|
||||
process func(Cmder) error
|
||||
processPipeline func([]Cmder) error
|
||||
processTxPipeline func([]Cmder) error
|
||||
|
||||
onClose func() error // hook called when client is closed
|
||||
|
||||
ctx context.Context
|
||||
|
||||
5
vendor/github.com/go-redis/redis/redis_no_context.go
сгенерированный
поставляемый
5
vendor/github.com/go-redis/redis/redis_no_context.go
сгенерированный
поставляемый
@@ -10,6 +10,9 @@ type baseClient struct {
|
||||
connPool pool.Pooler
|
||||
opt *Options
|
||||
|
||||
process func(Cmder) error
|
||||
process func(Cmder) error
|
||||
processPipeline func([]Cmder) error
|
||||
processTxPipeline func([]Cmder) error
|
||||
|
||||
onClose func() error // hook called when client is closed
|
||||
}
|
||||
|
||||
29
vendor/github.com/go-redis/redis/ring.go
сгенерированный
поставляемый
29
vendor/github.com/go-redis/redis/ring.go
сгенерированный
поставляемый
@@ -150,6 +150,8 @@ type Ring struct {
|
||||
shards map[string]*ringShard
|
||||
shardsList []*ringShard
|
||||
|
||||
processPipeline func([]Cmder) error
|
||||
|
||||
cmdsInfoOnce internal.Once
|
||||
cmdsInfo map[string]*CommandInfo
|
||||
|
||||
@@ -158,7 +160,9 @@ type Ring struct {
|
||||
|
||||
func NewRing(opt *RingOptions) *Ring {
|
||||
const nreplicas = 100
|
||||
|
||||
opt.init()
|
||||
|
||||
ring := &Ring{
|
||||
opt: opt,
|
||||
nreplicas: nreplicas,
|
||||
@@ -166,13 +170,17 @@ func NewRing(opt *RingOptions) *Ring {
|
||||
hash: consistenthash.New(nreplicas, nil),
|
||||
shards: make(map[string]*ringShard),
|
||||
}
|
||||
ring.setProcessor(ring.Process)
|
||||
ring.processPipeline = ring.defaultProcessPipeline
|
||||
ring.cmdable.setProcessor(ring.Process)
|
||||
|
||||
for name, addr := range opt.Addrs {
|
||||
clopt := opt.clientOptions()
|
||||
clopt.Addr = addr
|
||||
ring.addShard(name, NewClient(clopt))
|
||||
}
|
||||
|
||||
go ring.heartbeat()
|
||||
|
||||
return ring
|
||||
}
|
||||
|
||||
@@ -354,6 +362,13 @@ func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
|
||||
return c.shardByKey(firstKey)
|
||||
}
|
||||
|
||||
func (c *Ring) WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error) {
|
||||
c.ForEachShard(func(c *Client) error {
|
||||
c.WrapProcess(fn)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Ring) Process(cmd Cmder) error {
|
||||
shard, err := c.cmdShard(cmd)
|
||||
if err != nil {
|
||||
@@ -436,9 +451,9 @@ func (c *Ring) Close() error {
|
||||
|
||||
func (c *Ring) Pipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExec,
|
||||
exec: c.processPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.cmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
@@ -446,7 +461,13 @@ func (c *Ring) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||
return c.Pipeline().Pipelined(fn)
|
||||
}
|
||||
|
||||
func (c *Ring) pipelineExec(cmds []Cmder) error {
|
||||
func (c *Ring) WrapProcessPipeline(
|
||||
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
|
||||
) {
|
||||
c.processPipeline = fn(c.processPipeline)
|
||||
}
|
||||
|
||||
func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
|
||||
cmdsMap := make(map[string][]Cmder)
|
||||
for _, cmd := range cmds {
|
||||
cmdInfo := c.cmdInfo(cmd.Name())
|
||||
|
||||
14
vendor/github.com/go-redis/redis/sentinel.go
сгенерированный
поставляемый
14
vendor/github.com/go-redis/redis/sentinel.go
сгенерированный
поставляемый
@@ -76,7 +76,7 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
|
||||
opt: opt,
|
||||
}
|
||||
|
||||
client := Client{
|
||||
c := Client{
|
||||
baseClient: baseClient{
|
||||
opt: opt,
|
||||
connPool: failover.Pool(),
|
||||
@@ -86,9 +86,10 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
|
||||
},
|
||||
},
|
||||
}
|
||||
client.setProcessor(client.Process)
|
||||
c.baseClient.init()
|
||||
c.setProcessor(c.Process)
|
||||
|
||||
return &client
|
||||
return &c
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -100,14 +101,15 @@ type sentinelClient struct {
|
||||
|
||||
func newSentinel(opt *Options) *sentinelClient {
|
||||
opt.init()
|
||||
client := sentinelClient{
|
||||
c := sentinelClient{
|
||||
baseClient: baseClient{
|
||||
opt: opt,
|
||||
connPool: newConnPool(opt),
|
||||
},
|
||||
}
|
||||
client.cmdable = cmdable{client.Process}
|
||||
return &client
|
||||
c.baseClient.init()
|
||||
c.cmdable.setProcessor(c.Process)
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *sentinelClient) PubSub() *PubSub {
|
||||
|
||||
11
vendor/github.com/go-redis/redis/tx.go
сгенерированный
поставляемый
11
vendor/github.com/go-redis/redis/tx.go
сгенерированный
поставляемый
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/go-redis/redis/internal/pool"
|
||||
)
|
||||
|
||||
// Redis transaction failed.
|
||||
// TxFailedErr transaction redis failed.
|
||||
const TxFailedErr = internal.RedisError("redis: transaction failed")
|
||||
|
||||
// Tx implements Redis transactions as described in
|
||||
@@ -24,7 +24,8 @@ func (c *Client) newTx() *Tx {
|
||||
connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), true),
|
||||
},
|
||||
}
|
||||
tx.setProcessor(tx.Process)
|
||||
tx.baseClient.init()
|
||||
tx.statefulCmdable.setProcessor(tx.Process)
|
||||
return &tx
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ func (c *Client) Watch(fn func(*Tx) error, keys ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// close closes the transaction, releasing any open resources.
|
||||
// Close closes the transaction, releasing any open resources.
|
||||
func (c *Tx) Close() error {
|
||||
_ = c.Unwatch().Err()
|
||||
return c.baseClient.Close()
|
||||
@@ -75,9 +76,9 @@ func (c *Tx) Unwatch(keys ...string) *StatusCmd {
|
||||
|
||||
func (c *Tx) Pipeline() Pipeliner {
|
||||
pipe := Pipeline{
|
||||
exec: c.pipelineExecer(c.txPipelineProcessCmds),
|
||||
exec: c.processTxPipeline,
|
||||
}
|
||||
pipe.setProcessor(pipe.Process)
|
||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
||||
return &pipe
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/golang/protobuf/proto/extensions_test.go
сгенерированный
поставляемый
2
vendor/github.com/golang/protobuf/proto/extensions_test.go
сгенерированный
поставляемый
@@ -478,7 +478,7 @@ func TestUnmarshalRepeatingNonRepeatedExtension(t *testing.T) {
|
||||
t.Fatalf("[%s] Invalid extension", test.name)
|
||||
}
|
||||
if !reflect.DeepEqual(*ext, want) {
|
||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %s want: %s\n", test.name, ext, want)
|
||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %s want: %s\n", test.name, ext, &want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go
сгенерированный
поставляемый
6
vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go
сгенерированный
поставляемый
@@ -2029,7 +2029,11 @@ func (g *Generator) generateMessage(message *Descriptor) {
|
||||
// TODO: Revisit this and consider reverting back to anonymous interfaces.
|
||||
for oi := range message.OneofDecl {
|
||||
dname := oneofDisc[int32(oi)]
|
||||
g.P("type ", dname, " interface { ", dname, "() }")
|
||||
g.P("type ", dname, " interface {")
|
||||
g.In()
|
||||
g.P(dname, "()")
|
||||
g.Out()
|
||||
g.P("}")
|
||||
}
|
||||
g.P()
|
||||
for _, field := range message.Field {
|
||||
|
||||
18
vendor/github.com/gorilla/schema/.travis.yml
сгенерированный
поставляемый
Обычный файл
18
vendor/github.com/gorilla/schema/.travis.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,18 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.5
|
||||
- go: 1.6
|
||||
- go: 1.7
|
||||
- go: 1.8
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- go vet $(go list ./... | grep -v /vendor/)
|
||||
- go test -v -race ./...
|
||||
8
vendor/github.com/rsc/letsencrypt/LICENSE → vendor/github.com/gorilla/schema/LICENSE
сгенерированный
поставляемый
8
vendor/github.com/rsc/letsencrypt/LICENSE → vendor/github.com/gorilla/schema/LICENSE
сгенерированный
поставляемый
@@ -1,16 +1,16 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
90
vendor/github.com/gorilla/schema/README.md
сгенерированный
поставляемый
Обычный файл
90
vendor/github.com/gorilla/schema/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,90 @@
|
||||
schema
|
||||
======
|
||||
[](https://godoc.org/github.com/gorilla/schema) [](https://travis-ci.org/gorilla/schema)
|
||||
[](https://sourcegraph.com/github.com/gorilla/schema?badge)
|
||||
|
||||
|
||||
Package gorilla/schema converts structs to and from form values.
|
||||
|
||||
## Example
|
||||
|
||||
Here's a quick example: we parse POST form values and then decode them into a struct:
|
||||
|
||||
```go
|
||||
// Set a Decoder instance as a package global, because it caches
|
||||
// meta-data about structs, and an instance can be shared safely.
|
||||
var decoder = schema.NewDecoder()
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Phone string
|
||||
}
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
var person Person
|
||||
|
||||
// r.PostForm is a map of our POST form values
|
||||
err := decoder.Decode(&person, r.PostForm)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Do something with person.Name or person.Phone
|
||||
}
|
||||
```
|
||||
|
||||
Conversely, contents of a struct can be encoded into form values. Here's a variant of the previous example using the Encoder:
|
||||
|
||||
```go
|
||||
var encoder = schema.NewEncoder()
|
||||
|
||||
func MyHttpRequest() {
|
||||
person := Person{"Jane Doe", "555-5555"}
|
||||
form := url.Values{}
|
||||
|
||||
err := encoder.Encode(person, form)
|
||||
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Use form values, for example, with an http client
|
||||
client := new(http.Client)
|
||||
res, err := client.PostForm("http://my-api.test", form)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To define custom names for fields, use a struct tag "schema". To not populate certain fields, use a dash for the name and it will be ignored:
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string `schema:"name"` // custom name
|
||||
Phone string `schema:"phone"` // custom name
|
||||
Admin bool `schema:"-"` // this field is never set
|
||||
}
|
||||
```
|
||||
|
||||
The supported field types in the struct are:
|
||||
|
||||
* bool
|
||||
* float variants (float32, float64)
|
||||
* int variants (int, int8, int16, int32, int64)
|
||||
* string
|
||||
* uint variants (uint, uint8, uint16, uint32, uint64)
|
||||
* struct
|
||||
* a pointer to one of the above types
|
||||
* a slice or a pointer to a slice of one of the above types
|
||||
|
||||
Unsupported types are simply ignored, however custom types can be registered to be converted.
|
||||
|
||||
More examples are available on the Gorilla website: http://www.gorillatoolkit.org/pkg/schema
|
||||
|
||||
## License
|
||||
|
||||
BSD licensed. See the LICENSE file for details.
|
||||
264
vendor/github.com/gorilla/schema/cache.go
сгенерированный
поставляемый
Обычный файл
264
vendor/github.com/gorilla/schema/cache.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,264 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var invalidPath = errors.New("schema: invalid path")
|
||||
|
||||
// newCache returns a new cache.
|
||||
func newCache() *cache {
|
||||
c := cache{
|
||||
m: make(map[reflect.Type]*structInfo),
|
||||
regconv: make(map[reflect.Type]Converter),
|
||||
tag: "schema",
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
// cache caches meta-data about a struct.
|
||||
type cache struct {
|
||||
l sync.RWMutex
|
||||
m map[reflect.Type]*structInfo
|
||||
regconv map[reflect.Type]Converter
|
||||
tag string
|
||||
}
|
||||
|
||||
// registerConverter registers a converter function for a custom type.
|
||||
func (c *cache) registerConverter(value interface{}, converterFunc Converter) {
|
||||
c.regconv[reflect.TypeOf(value)] = converterFunc
|
||||
}
|
||||
|
||||
// parsePath parses a path in dotted notation verifying that it is a valid
|
||||
// path to a struct field.
|
||||
//
|
||||
// It returns "path parts" which contain indices to fields to be used by
|
||||
// reflect.Value.FieldByString(). Multiple parts are required for slices of
|
||||
// structs.
|
||||
func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
|
||||
var struc *structInfo
|
||||
var field *fieldInfo
|
||||
var index64 int64
|
||||
var err error
|
||||
parts := make([]pathPart, 0)
|
||||
path := make([]string, 0)
|
||||
keys := strings.Split(p, ".")
|
||||
for i := 0; i < len(keys); i++ {
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, invalidPath
|
||||
}
|
||||
if struc = c.get(t); struc == nil {
|
||||
return nil, invalidPath
|
||||
}
|
||||
if field = struc.get(keys[i]); field == nil {
|
||||
return nil, invalidPath
|
||||
}
|
||||
// Valid field. Append index.
|
||||
path = append(path, field.name)
|
||||
if field.ss {
|
||||
// Parse a special case: slices of structs.
|
||||
// i+1 must be the slice index.
|
||||
//
|
||||
// Now that struct can implements TextUnmarshaler interface,
|
||||
// we don't need to force the struct's fields to appear in the path.
|
||||
// So checking i+2 is not necessary anymore.
|
||||
i++
|
||||
if i+1 > len(keys) {
|
||||
return nil, invalidPath
|
||||
}
|
||||
if index64, err = strconv.ParseInt(keys[i], 10, 0); err != nil {
|
||||
return nil, invalidPath
|
||||
}
|
||||
parts = append(parts, pathPart{
|
||||
path: path,
|
||||
field: field,
|
||||
index: int(index64),
|
||||
})
|
||||
path = make([]string, 0)
|
||||
|
||||
// Get the next struct type, dropping ptrs.
|
||||
if field.typ.Kind() == reflect.Ptr {
|
||||
t = field.typ.Elem()
|
||||
} else {
|
||||
t = field.typ
|
||||
}
|
||||
if t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
}
|
||||
} else if field.typ.Kind() == reflect.Ptr {
|
||||
t = field.typ.Elem()
|
||||
} else {
|
||||
t = field.typ
|
||||
}
|
||||
}
|
||||
// Add the remaining.
|
||||
parts = append(parts, pathPart{
|
||||
path: path,
|
||||
field: field,
|
||||
index: -1,
|
||||
})
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// get returns a cached structInfo, creating it if necessary.
|
||||
func (c *cache) get(t reflect.Type) *structInfo {
|
||||
c.l.RLock()
|
||||
info := c.m[t]
|
||||
c.l.RUnlock()
|
||||
if info == nil {
|
||||
info = c.create(t, nil)
|
||||
c.l.Lock()
|
||||
c.m[t] = info
|
||||
c.l.Unlock()
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// create creates a structInfo with meta-data about a struct.
|
||||
func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
|
||||
if info == nil {
|
||||
info = &structInfo{fields: []*fieldInfo{}}
|
||||
}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.Anonymous {
|
||||
ft := field.Type
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
if ft.Kind() == reflect.Struct {
|
||||
bef := len(info.fields)
|
||||
c.create(ft, info)
|
||||
for _, fi := range info.fields[bef:len(info.fields)] {
|
||||
// exclude required check because duplicated to embedded field
|
||||
fi.required = false
|
||||
}
|
||||
}
|
||||
}
|
||||
c.createField(field, info)
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// createField creates a fieldInfo for the given field.
|
||||
func (c *cache) createField(field reflect.StructField, info *structInfo) {
|
||||
alias, options := fieldAlias(field, c.tag)
|
||||
if alias == "-" {
|
||||
// Ignore this field.
|
||||
return
|
||||
}
|
||||
// Check if the type is supported and don't cache it if not.
|
||||
// First let's get the basic type.
|
||||
isSlice, isStruct := false, false
|
||||
ft := field.Type
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
if isSlice = ft.Kind() == reflect.Slice; isSlice {
|
||||
ft = ft.Elem()
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
}
|
||||
if ft.Kind() == reflect.Array {
|
||||
ft = ft.Elem()
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
}
|
||||
if isStruct = ft.Kind() == reflect.Struct; !isStruct {
|
||||
if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
|
||||
// Type is not supported.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
info.fields = append(info.fields, &fieldInfo{
|
||||
typ: field.Type,
|
||||
name: field.Name,
|
||||
ss: isSlice && isStruct,
|
||||
alias: alias,
|
||||
anon: field.Anonymous,
|
||||
required: options.Contains("required"),
|
||||
})
|
||||
}
|
||||
|
||||
// converter returns the converter for a type.
|
||||
func (c *cache) converter(t reflect.Type) Converter {
|
||||
return c.regconv[t]
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type structInfo struct {
|
||||
fields []*fieldInfo
|
||||
}
|
||||
|
||||
func (i *structInfo) get(alias string) *fieldInfo {
|
||||
for _, field := range i.fields {
|
||||
if strings.EqualFold(field.alias, alias) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
typ reflect.Type
|
||||
name string // field name in the struct.
|
||||
ss bool // true if this is a slice of structs.
|
||||
alias string
|
||||
anon bool // is an embedded field
|
||||
required bool // tag option
|
||||
}
|
||||
|
||||
type pathPart struct {
|
||||
field *fieldInfo
|
||||
path []string // path to the field: walks structs using field names.
|
||||
index int // struct index in slices of structs.
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// fieldAlias parses a field tag to get a field alias.
|
||||
func fieldAlias(field reflect.StructField, tagName string) (alias string, options tagOptions) {
|
||||
if tag := field.Tag.Get(tagName); tag != "" {
|
||||
alias, options = parseTag(tag)
|
||||
}
|
||||
if alias == "" {
|
||||
alias = field.Name
|
||||
}
|
||||
return alias, options
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's tag, or
|
||||
// the empty string. It does not include the leading comma.
|
||||
type tagOptions []string
|
||||
|
||||
// parseTag splits a struct field's url tag into its name and comma-separated
|
||||
// options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
s := strings.Split(tag, ",")
|
||||
return s[0], s[1:]
|
||||
}
|
||||
|
||||
// Contains checks whether the tagOptions contains the specified option.
|
||||
func (o tagOptions) Contains(option string) bool {
|
||||
for _, s := range o {
|
||||
if s == option {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
145
vendor/github.com/gorilla/schema/converter.go
сгенерированный
поставляемый
Обычный файл
145
vendor/github.com/gorilla/schema/converter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,145 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Converter func(string) reflect.Value
|
||||
|
||||
var (
|
||||
invalidValue = reflect.Value{}
|
||||
boolType = reflect.Bool
|
||||
float32Type = reflect.Float32
|
||||
float64Type = reflect.Float64
|
||||
intType = reflect.Int
|
||||
int8Type = reflect.Int8
|
||||
int16Type = reflect.Int16
|
||||
int32Type = reflect.Int32
|
||||
int64Type = reflect.Int64
|
||||
stringType = reflect.String
|
||||
uintType = reflect.Uint
|
||||
uint8Type = reflect.Uint8
|
||||
uint16Type = reflect.Uint16
|
||||
uint32Type = reflect.Uint32
|
||||
uint64Type = reflect.Uint64
|
||||
)
|
||||
|
||||
// Default converters for basic types.
|
||||
var builtinConverters = map[reflect.Kind]Converter{
|
||||
boolType: convertBool,
|
||||
float32Type: convertFloat32,
|
||||
float64Type: convertFloat64,
|
||||
intType: convertInt,
|
||||
int8Type: convertInt8,
|
||||
int16Type: convertInt16,
|
||||
int32Type: convertInt32,
|
||||
int64Type: convertInt64,
|
||||
stringType: convertString,
|
||||
uintType: convertUint,
|
||||
uint8Type: convertUint8,
|
||||
uint16Type: convertUint16,
|
||||
uint32Type: convertUint32,
|
||||
uint64Type: convertUint64,
|
||||
}
|
||||
|
||||
func convertBool(value string) reflect.Value {
|
||||
if value == "on" {
|
||||
return reflect.ValueOf(true)
|
||||
} else if v, err := strconv.ParseBool(value); err == nil {
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertFloat32(value string) reflect.Value {
|
||||
if v, err := strconv.ParseFloat(value, 32); err == nil {
|
||||
return reflect.ValueOf(float32(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertFloat64(value string) reflect.Value {
|
||||
if v, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertInt(value string) reflect.Value {
|
||||
if v, err := strconv.ParseInt(value, 10, 0); err == nil {
|
||||
return reflect.ValueOf(int(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertInt8(value string) reflect.Value {
|
||||
if v, err := strconv.ParseInt(value, 10, 8); err == nil {
|
||||
return reflect.ValueOf(int8(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertInt16(value string) reflect.Value {
|
||||
if v, err := strconv.ParseInt(value, 10, 16); err == nil {
|
||||
return reflect.ValueOf(int16(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertInt32(value string) reflect.Value {
|
||||
if v, err := strconv.ParseInt(value, 10, 32); err == nil {
|
||||
return reflect.ValueOf(int32(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertInt64(value string) reflect.Value {
|
||||
if v, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertString(value string) reflect.Value {
|
||||
return reflect.ValueOf(value)
|
||||
}
|
||||
|
||||
func convertUint(value string) reflect.Value {
|
||||
if v, err := strconv.ParseUint(value, 10, 0); err == nil {
|
||||
return reflect.ValueOf(uint(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertUint8(value string) reflect.Value {
|
||||
if v, err := strconv.ParseUint(value, 10, 8); err == nil {
|
||||
return reflect.ValueOf(uint8(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertUint16(value string) reflect.Value {
|
||||
if v, err := strconv.ParseUint(value, 10, 16); err == nil {
|
||||
return reflect.ValueOf(uint16(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertUint32(value string) reflect.Value {
|
||||
if v, err := strconv.ParseUint(value, 10, 32); err == nil {
|
||||
return reflect.ValueOf(uint32(v))
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
|
||||
func convertUint64(value string) reflect.Value {
|
||||
if v, err := strconv.ParseUint(value, 10, 64); err == nil {
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
return invalidValue
|
||||
}
|
||||
420
vendor/github.com/gorilla/schema/decoder.go
сгенерированный
поставляемый
Обычный файл
420
vendor/github.com/gorilla/schema/decoder.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,420 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewDecoder returns a new Decoder.
|
||||
func NewDecoder() *Decoder {
|
||||
return &Decoder{cache: newCache()}
|
||||
}
|
||||
|
||||
// Decoder decodes values from a map[string][]string to a struct.
|
||||
type Decoder struct {
|
||||
cache *cache
|
||||
zeroEmpty bool
|
||||
ignoreUnknownKeys bool
|
||||
}
|
||||
|
||||
// SetAliasTag changes the tag used to locate custom field aliases.
|
||||
// The default tag is "schema".
|
||||
func (d *Decoder) SetAliasTag(tag string) {
|
||||
d.cache.tag = tag
|
||||
}
|
||||
|
||||
// ZeroEmpty controls the behaviour when the decoder encounters empty values
|
||||
// in a map.
|
||||
// If z is true and a key in the map has the empty string as a value
|
||||
// then the corresponding struct field is set to the zero value.
|
||||
// If z is false then empty strings are ignored.
|
||||
//
|
||||
// The default value is false, that is empty values do not change
|
||||
// the value of the struct field.
|
||||
func (d *Decoder) ZeroEmpty(z bool) {
|
||||
d.zeroEmpty = z
|
||||
}
|
||||
|
||||
// IgnoreUnknownKeys controls the behaviour when the decoder encounters unknown
|
||||
// keys in the map.
|
||||
// If i is true and an unknown field is encountered, it is ignored. This is
|
||||
// similar to how unknown keys are handled by encoding/json.
|
||||
// If i is false then Decode will return an error. Note that any valid keys
|
||||
// will still be decoded in to the target struct.
|
||||
//
|
||||
// To preserve backwards compatibility, the default value is false.
|
||||
func (d *Decoder) IgnoreUnknownKeys(i bool) {
|
||||
d.ignoreUnknownKeys = i
|
||||
}
|
||||
|
||||
// RegisterConverter registers a converter function for a custom type.
|
||||
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
|
||||
d.cache.registerConverter(value, converterFunc)
|
||||
}
|
||||
|
||||
// Decode decodes a map[string][]string to a struct.
|
||||
//
|
||||
// The first parameter must be a pointer to a struct.
|
||||
//
|
||||
// The second parameter is a map, typically url.Values from an HTTP request.
|
||||
// Keys are "paths" in dotted notation to the struct fields and nested structs.
|
||||
//
|
||||
// See the package documentation for a full explanation of the mechanics.
|
||||
func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
|
||||
v := reflect.ValueOf(dst)
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
return errors.New("schema: interface must be a pointer to struct")
|
||||
}
|
||||
v = v.Elem()
|
||||
t := v.Type()
|
||||
errors := MultiError{}
|
||||
for path, values := range src {
|
||||
if parts, err := d.cache.parsePath(path, t); err == nil {
|
||||
if err = d.decode(v, path, parts, values); err != nil {
|
||||
errors[path] = err
|
||||
}
|
||||
} else if !d.ignoreUnknownKeys {
|
||||
errors[path] = fmt.Errorf("schema: invalid path %q", path)
|
||||
}
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return errors
|
||||
}
|
||||
return d.checkRequired(t, src, "")
|
||||
}
|
||||
|
||||
// checkRequired checks whether required fields are empty
|
||||
//
|
||||
// check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation
|
||||
//
|
||||
// src is the source map for decoding, we use it here to see if those required fields are included in src
|
||||
func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix string) error {
|
||||
struc := d.cache.get(t)
|
||||
if struc == nil {
|
||||
// unexpect, cache.get never return nil
|
||||
return errors.New("cache fail")
|
||||
}
|
||||
|
||||
for _, f := range struc.fields {
|
||||
if f.typ.Kind() == reflect.Struct {
|
||||
err := d.checkRequired(f.typ, src, prefix+f.alias+".")
|
||||
if err != nil {
|
||||
if !f.anon {
|
||||
return err
|
||||
}
|
||||
// check embedded parent field.
|
||||
err2 := d.checkRequired(f.typ, src, prefix)
|
||||
if err2 != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if f.required {
|
||||
key := f.alias
|
||||
if prefix != "" {
|
||||
key = prefix + key
|
||||
}
|
||||
if isEmpty(f.typ, src[key]) {
|
||||
return fmt.Errorf("%v is empty", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isEmpty returns true if value is empty for specific type
|
||||
func isEmpty(t reflect.Type, value []string) bool {
|
||||
if len(value) == 0 {
|
||||
return true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case boolType, float32Type, float64Type, intType, int8Type, int32Type, int64Type, stringType, uint8Type, uint16Type, uint32Type, uint64Type:
|
||||
return len(value[0]) == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// decode fills a struct field using a parsed path.
|
||||
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values []string) error {
|
||||
// Get the field walking the struct fields by index.
|
||||
for _, name := range parts[0].path {
|
||||
if v.Type().Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
v = v.FieldByName(name)
|
||||
}
|
||||
// Don't even bother for unexported fields.
|
||||
if !v.CanSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dereference if needed.
|
||||
t := v.Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(t))
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// Slice of structs. Let's go recursive.
|
||||
if len(parts) > 1 {
|
||||
idx := parts[0].index
|
||||
if v.IsNil() || v.Len() < idx+1 {
|
||||
value := reflect.MakeSlice(t, idx+1, idx+1)
|
||||
if v.Len() < idx+1 {
|
||||
// Resize it.
|
||||
reflect.Copy(value, v)
|
||||
}
|
||||
v.Set(value)
|
||||
}
|
||||
return d.decode(v.Index(idx), path, parts[1:], values)
|
||||
}
|
||||
|
||||
// Get the converter early in case there is one for a slice type.
|
||||
conv := d.cache.converter(t)
|
||||
m := isTextUnmarshaler(v)
|
||||
if conv == nil && t.Kind() == reflect.Slice && m.IsSlice {
|
||||
var items []reflect.Value
|
||||
elemT := t.Elem()
|
||||
isPtrElem := elemT.Kind() == reflect.Ptr
|
||||
if isPtrElem {
|
||||
elemT = elemT.Elem()
|
||||
}
|
||||
|
||||
// Try to get a converter for the element type.
|
||||
conv := d.cache.converter(elemT)
|
||||
if conv == nil {
|
||||
conv = builtinConverters[elemT.Kind()]
|
||||
if conv == nil {
|
||||
// As we are not dealing with slice of structs here, we don't need to check if the type
|
||||
// implements TextUnmarshaler interface
|
||||
return fmt.Errorf("schema: converter not found for %v", elemT)
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range values {
|
||||
if value == "" {
|
||||
if d.zeroEmpty {
|
||||
items = append(items, reflect.Zero(elemT))
|
||||
}
|
||||
} else if m.IsValid {
|
||||
u := reflect.New(elemT)
|
||||
if m.IsPtr {
|
||||
u = reflect.New(reflect.PtrTo(elemT).Elem())
|
||||
}
|
||||
if err := u.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)); err != nil {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: t,
|
||||
Index: key,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
if m.IsPtr {
|
||||
items = append(items, u.Elem().Addr())
|
||||
} else if u.Kind() == reflect.Ptr {
|
||||
items = append(items, u.Elem())
|
||||
} else {
|
||||
items = append(items, u)
|
||||
}
|
||||
} else if item := conv(value); item.IsValid() {
|
||||
if isPtrElem {
|
||||
ptr := reflect.New(elemT)
|
||||
ptr.Elem().Set(item)
|
||||
item = ptr
|
||||
}
|
||||
if item.Type() != elemT && !isPtrElem {
|
||||
item = item.Convert(elemT)
|
||||
}
|
||||
items = append(items, item)
|
||||
} else {
|
||||
if strings.Contains(value, ",") {
|
||||
values := strings.Split(value, ",")
|
||||
for _, value := range values {
|
||||
if value == "" {
|
||||
if d.zeroEmpty {
|
||||
items = append(items, reflect.Zero(elemT))
|
||||
}
|
||||
} else if item := conv(value); item.IsValid() {
|
||||
if isPtrElem {
|
||||
ptr := reflect.New(elemT)
|
||||
ptr.Elem().Set(item)
|
||||
item = ptr
|
||||
}
|
||||
if item.Type() != elemT && !isPtrElem {
|
||||
item = item.Convert(elemT)
|
||||
}
|
||||
items = append(items, item)
|
||||
} else {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: elemT,
|
||||
Index: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: elemT,
|
||||
Index: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...)
|
||||
v.Set(value)
|
||||
} else {
|
||||
val := ""
|
||||
// Use the last value provided if any values were provided
|
||||
if len(values) > 0 {
|
||||
val = values[len(values)-1]
|
||||
}
|
||||
|
||||
if val == "" {
|
||||
if d.zeroEmpty {
|
||||
v.Set(reflect.Zero(t))
|
||||
}
|
||||
} else if conv != nil {
|
||||
if value := conv(val); value.IsValid() {
|
||||
v.Set(value.Convert(t))
|
||||
} else {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: t,
|
||||
Index: -1,
|
||||
}
|
||||
}
|
||||
} else if m.IsValid {
|
||||
// If the value implements the encoding.TextUnmarshaler interface
|
||||
// apply UnmarshalText as the converter
|
||||
if err := m.Unmarshaler.UnmarshalText([]byte(val)); err != nil {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: t,
|
||||
Index: -1,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
} else if conv := builtinConverters[t.Kind()]; conv != nil {
|
||||
if value := conv(val); value.IsValid() {
|
||||
v.Set(value.Convert(t))
|
||||
} else {
|
||||
return ConversionError{
|
||||
Key: path,
|
||||
Type: t,
|
||||
Index: -1,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("schema: converter not found for %v", t)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isTextUnmarshaler(v reflect.Value) unmarshaler {
|
||||
|
||||
// Create a new unmarshaller instance
|
||||
m := unmarshaler{}
|
||||
|
||||
// As the UnmarshalText function should be applied
|
||||
// to the pointer of the type, we convert the value to pointer.
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
if m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
|
||||
return m
|
||||
}
|
||||
|
||||
// if v is []T or *[]T create new T
|
||||
t := v.Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() == reflect.Slice {
|
||||
// if t is a pointer slice, check if it implements encoding.TextUnmarshaler
|
||||
m.IsSlice = true
|
||||
if t = t.Elem(); t.Kind() == reflect.Ptr {
|
||||
t = reflect.PtrTo(t.Elem())
|
||||
v = reflect.Zero(t)
|
||||
m.IsPtr = true
|
||||
m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
v = reflect.New(t)
|
||||
m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
|
||||
return m
|
||||
}
|
||||
|
||||
// TextUnmarshaler helpers ----------------------------------------------------
|
||||
// unmarshaller contains information about a TextUnmarshaler type
|
||||
type unmarshaler struct {
|
||||
Unmarshaler encoding.TextUnmarshaler
|
||||
IsSlice bool
|
||||
IsPtr bool
|
||||
IsValid bool
|
||||
}
|
||||
|
||||
// Errors ---------------------------------------------------------------------
|
||||
|
||||
// ConversionError stores information about a failed conversion.
|
||||
type ConversionError struct {
|
||||
Key string // key from the source map.
|
||||
Type reflect.Type // expected type of elem
|
||||
Index int // index for multi-value fields; -1 for single-value fields.
|
||||
Err error // low-level error (when it exists)
|
||||
}
|
||||
|
||||
func (e ConversionError) Error() string {
|
||||
var output string
|
||||
|
||||
if e.Index < 0 {
|
||||
output = fmt.Sprintf("schema: error converting value for %q", e.Key)
|
||||
} else {
|
||||
output = fmt.Sprintf("schema: error converting value for index %d of %q",
|
||||
e.Index, e.Key)
|
||||
}
|
||||
|
||||
if e.Err != nil {
|
||||
output = fmt.Sprintf("%s. Details: %s", output, e.Err)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// MultiError stores multiple decoding errors.
|
||||
//
|
||||
// Borrowed from the App Engine SDK.
|
||||
type MultiError map[string]error
|
||||
|
||||
func (e MultiError) Error() string {
|
||||
s := ""
|
||||
for _, err := range e {
|
||||
s = err.Error()
|
||||
break
|
||||
}
|
||||
switch len(e) {
|
||||
case 0:
|
||||
return "(0 errors)"
|
||||
case 1:
|
||||
return s
|
||||
case 2:
|
||||
return s + " (and 1 other error)"
|
||||
}
|
||||
return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1)
|
||||
}
|
||||
1693
vendor/github.com/gorilla/schema/decoder_test.go
сгенерированный
поставляемый
Обычный файл
1693
vendor/github.com/gorilla/schema/decoder_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
148
vendor/github.com/gorilla/schema/doc.go
сгенерированный
поставляемый
Обычный файл
148
vendor/github.com/gorilla/schema/doc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,148 @@
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package gorilla/schema fills a struct with form values.
|
||||
|
||||
The basic usage is really simple. Given this struct:
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Phone string
|
||||
}
|
||||
|
||||
...we can fill it passing a map to the Decode() function:
|
||||
|
||||
values := map[string][]string{
|
||||
"Name": {"John"},
|
||||
"Phone": {"999-999-999"},
|
||||
}
|
||||
person := new(Person)
|
||||
decoder := schema.NewDecoder()
|
||||
decoder.Decode(person, values)
|
||||
|
||||
This is just a simple example and it doesn't make a lot of sense to create
|
||||
the map manually. Typically it will come from a http.Request object and
|
||||
will be of type url.Values, http.Request.Form, or http.Request.MultipartForm:
|
||||
|
||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
decoder := schema.NewDecoder()
|
||||
// r.PostForm is a map of our POST form values
|
||||
err := decoder.Decode(person, r.PostForm)
|
||||
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Do something with person.Name or person.Phone
|
||||
}
|
||||
|
||||
Note: it is a good idea to set a Decoder instance as a package global,
|
||||
because it caches meta-data about structs, and an instance can be shared safely:
|
||||
|
||||
var decoder = schema.NewDecoder()
|
||||
|
||||
To define custom names for fields, use a struct tag "schema". To not populate
|
||||
certain fields, use a dash for the name and it will be ignored:
|
||||
|
||||
type Person struct {
|
||||
Name string `schema:"name"` // custom name
|
||||
Phone string `schema:"phone"` // custom name
|
||||
Admin bool `schema:"-"` // this field is never set
|
||||
}
|
||||
|
||||
The supported field types in the destination struct are:
|
||||
|
||||
* bool
|
||||
* float variants (float32, float64)
|
||||
* int variants (int, int8, int16, int32, int64)
|
||||
* string
|
||||
* uint variants (uint, uint8, uint16, uint32, uint64)
|
||||
* struct
|
||||
* a pointer to one of the above types
|
||||
* a slice or a pointer to a slice of one of the above types
|
||||
|
||||
Non-supported types are simply ignored, however custom types can be registered
|
||||
to be converted.
|
||||
|
||||
To fill nested structs, keys must use a dotted notation as the "path" for the
|
||||
field. So for example, to fill the struct Person below:
|
||||
|
||||
type Phone struct {
|
||||
Label string
|
||||
Number string
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Phone Phone
|
||||
}
|
||||
|
||||
...the source map must have the keys "Name", "Phone.Label" and "Phone.Number".
|
||||
This means that an HTML form to fill a Person struct must look like this:
|
||||
|
||||
<form>
|
||||
<input type="text" name="Name">
|
||||
<input type="text" name="Phone.Label">
|
||||
<input type="text" name="Phone.Number">
|
||||
</form>
|
||||
|
||||
Single values are filled using the first value for a key from the source map.
|
||||
Slices are filled using all values for a key from the source map. So to fill
|
||||
a Person with multiple Phone values, like:
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Phones []Phone
|
||||
}
|
||||
|
||||
...an HTML form that accepts three Phone values would look like this:
|
||||
|
||||
<form>
|
||||
<input type="text" name="Name">
|
||||
<input type="text" name="Phones.0.Label">
|
||||
<input type="text" name="Phones.0.Number">
|
||||
<input type="text" name="Phones.1.Label">
|
||||
<input type="text" name="Phones.1.Number">
|
||||
<input type="text" name="Phones.2.Label">
|
||||
<input type="text" name="Phones.2.Number">
|
||||
</form>
|
||||
|
||||
Notice that only for slices of structs the slice index is required.
|
||||
This is needed for disambiguation: if the nested struct also had a slice
|
||||
field, we could not translate multiple values to it if we did not use an
|
||||
index for the parent struct.
|
||||
|
||||
There's also the possibility to create a custom type that implements the
|
||||
TextUnmarshaler interface, and in this case there's no need to register
|
||||
a converter, like:
|
||||
|
||||
type Person struct {
|
||||
Emails []Email
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
*mail.Address
|
||||
}
|
||||
|
||||
func (e *Email) UnmarshalText(text []byte) (err error) {
|
||||
e.Address, err = mail.ParseAddress(string(text))
|
||||
return
|
||||
}
|
||||
|
||||
...an HTML form that accepts three Email values would look like this:
|
||||
|
||||
<form>
|
||||
<input type="email" name="Emails.0">
|
||||
<input type="email" name="Emails.1">
|
||||
<input type="email" name="Emails.2">
|
||||
</form>
|
||||
*/
|
||||
package schema
|
||||
195
vendor/github.com/gorilla/schema/encoder.go
сгенерированный
поставляемый
Обычный файл
195
vendor/github.com/gorilla/schema/encoder.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,195 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type encoderFunc func(reflect.Value) string
|
||||
|
||||
// Encoder encodes values from a struct into url.Values.
|
||||
type Encoder struct {
|
||||
cache *cache
|
||||
regenc map[reflect.Type]encoderFunc
|
||||
}
|
||||
|
||||
// NewEncoder returns a new Encoder with defaults.
|
||||
func NewEncoder() *Encoder {
|
||||
return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
|
||||
}
|
||||
|
||||
// Encode encodes a struct into map[string][]string.
|
||||
//
|
||||
// Intended for use with url.Values.
|
||||
func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
|
||||
v := reflect.ValueOf(src)
|
||||
|
||||
return e.encode(v, dst)
|
||||
}
|
||||
|
||||
// RegisterEncoder registers a converter for encoding a custom type.
|
||||
func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
|
||||
e.regenc[reflect.TypeOf(value)] = encoder
|
||||
}
|
||||
|
||||
// SetAliasTag changes the tag used to locate custom field aliases.
|
||||
// The default tag is "schema".
|
||||
func (e *Encoder) SetAliasTag(tag string) {
|
||||
e.cache.tag = tag
|
||||
}
|
||||
|
||||
// isValidStructPointer test if input value is a valid struct pointer.
|
||||
func isValidStructPointer(v reflect.Value) bool {
|
||||
return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Func:
|
||||
case reflect.Map, reflect.Slice:
|
||||
return v.IsNil() || v.Len() == 0
|
||||
case reflect.Array:
|
||||
z := true
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
z = z && isZero(v.Index(i))
|
||||
}
|
||||
return z
|
||||
case reflect.Struct:
|
||||
z := true
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
z = z && isZero(v.Field(i))
|
||||
}
|
||||
return z
|
||||
}
|
||||
// Compare other types directly:
|
||||
z := reflect.Zero(v.Type())
|
||||
return v.Interface() == z.Interface()
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return errors.New("schema: interface must be a struct")
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
errors := MultiError{}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
name, opts := fieldAlias(t.Field(i), e.cache.tag)
|
||||
if name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Encode struct pointer types if the field is a valid pointer and a struct.
|
||||
if isValidStructPointer(v.Field(i)) {
|
||||
e.encode(v.Field(i).Elem(), dst)
|
||||
continue
|
||||
}
|
||||
|
||||
encFunc := typeEncoder(v.Field(i).Type(), e.regenc)
|
||||
|
||||
// Encode non-slice types and custom implementations immediately.
|
||||
if encFunc != nil {
|
||||
value := encFunc(v.Field(i))
|
||||
if opts.Contains("omitempty") && isZero(v.Field(i)) {
|
||||
continue
|
||||
}
|
||||
|
||||
dst[name] = append(dst[name], value)
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Field(i).Type().Kind() == reflect.Struct {
|
||||
e.encode(v.Field(i), dst)
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Field(i).Type().Kind() == reflect.Slice {
|
||||
encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc)
|
||||
}
|
||||
|
||||
if encFunc == nil {
|
||||
errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i))
|
||||
continue
|
||||
}
|
||||
|
||||
// Encode a slice.
|
||||
if v.Field(i).Len() == 0 && opts.Contains("omitempty") {
|
||||
continue
|
||||
}
|
||||
|
||||
dst[name] = []string{}
|
||||
for j := 0; j < v.Field(i).Len(); j++ {
|
||||
dst[name] = append(dst[name], encFunc(v.Field(i).Index(j)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
|
||||
if f, ok := reg[t]; ok {
|
||||
return f
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return encodeBool
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return encodeInt
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return encodeUint
|
||||
case reflect.Float32:
|
||||
return encodeFloat32
|
||||
case reflect.Float64:
|
||||
return encodeFloat64
|
||||
case reflect.Ptr:
|
||||
f := typeEncoder(t.Elem(), reg)
|
||||
return func(v reflect.Value) string {
|
||||
if v.IsNil() {
|
||||
return "null"
|
||||
}
|
||||
return f(v.Elem())
|
||||
}
|
||||
case reflect.String:
|
||||
return encodeString
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func encodeBool(v reflect.Value) string {
|
||||
return strconv.FormatBool(v.Bool())
|
||||
}
|
||||
|
||||
func encodeInt(v reflect.Value) string {
|
||||
return strconv.FormatInt(int64(v.Int()), 10)
|
||||
}
|
||||
|
||||
func encodeUint(v reflect.Value) string {
|
||||
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||
}
|
||||
|
||||
func encodeFloat(v reflect.Value, bits int) string {
|
||||
return strconv.FormatFloat(v.Float(), 'f', 6, bits)
|
||||
}
|
||||
|
||||
func encodeFloat32(v reflect.Value) string {
|
||||
return encodeFloat(v, 32)
|
||||
}
|
||||
|
||||
func encodeFloat64(v reflect.Value) string {
|
||||
return encodeFloat(v, 64)
|
||||
}
|
||||
|
||||
func encodeString(v reflect.Value) string {
|
||||
return v.String()
|
||||
}
|
||||
420
vendor/github.com/gorilla/schema/encoder_test.go
сгенерированный
поставляемый
Обычный файл
420
vendor/github.com/gorilla/schema/encoder_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,420 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type E1 struct {
|
||||
F01 int `schema:"f01"`
|
||||
F02 int `schema:"-"`
|
||||
F03 string `schema:"f03"`
|
||||
F04 string `schema:"f04,omitempty"`
|
||||
F05 bool `schema:"f05"`
|
||||
F06 bool `schema:"f06"`
|
||||
F07 *string `schema:"f07"`
|
||||
F08 *int8 `schema:"f08"`
|
||||
F09 float64 `schema:"f09"`
|
||||
F10 func() `schema:"f10"`
|
||||
F11 inner
|
||||
}
|
||||
type inner struct {
|
||||
F12 int
|
||||
}
|
||||
|
||||
func TestFilled(t *testing.T) {
|
||||
f07 := "seven"
|
||||
var f08 int8 = 8
|
||||
s := &E1{
|
||||
F01: 1,
|
||||
F02: 2,
|
||||
F03: "three",
|
||||
F04: "four",
|
||||
F05: true,
|
||||
F06: false,
|
||||
F07: &f07,
|
||||
F08: &f08,
|
||||
F09: 1.618,
|
||||
F10: func() {},
|
||||
F11: inner{12},
|
||||
}
|
||||
|
||||
vals := make(map[string][]string)
|
||||
errs := NewEncoder().Encode(s, vals)
|
||||
|
||||
valExists(t, "f01", "1", vals)
|
||||
valNotExists(t, "f02", vals)
|
||||
valExists(t, "f03", "three", vals)
|
||||
valExists(t, "f05", "true", vals)
|
||||
valExists(t, "f06", "false", vals)
|
||||
valExists(t, "f07", "seven", vals)
|
||||
valExists(t, "f08", "8", vals)
|
||||
valExists(t, "f09", "1.618000", vals)
|
||||
valExists(t, "F12", "12", vals)
|
||||
|
||||
emptyErr := MultiError{}
|
||||
if errs.Error() == emptyErr.Error() {
|
||||
t.Errorf("Expected error got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
type Aa int
|
||||
|
||||
type E3 struct {
|
||||
F01 bool `schema:"f01"`
|
||||
F02 float32 `schema:"f02"`
|
||||
F03 float64 `schema:"f03"`
|
||||
F04 int `schema:"f04"`
|
||||
F05 int8 `schema:"f05"`
|
||||
F06 int16 `schema:"f06"`
|
||||
F07 int32 `schema:"f07"`
|
||||
F08 int64 `schema:"f08"`
|
||||
F09 string `schema:"f09"`
|
||||
F10 uint `schema:"f10"`
|
||||
F11 uint8 `schema:"f11"`
|
||||
F12 uint16 `schema:"f12"`
|
||||
F13 uint32 `schema:"f13"`
|
||||
F14 uint64 `schema:"f14"`
|
||||
F15 Aa `schema:"f15"`
|
||||
}
|
||||
|
||||
// Test compatibility with default decoder types.
|
||||
func TestCompat(t *testing.T) {
|
||||
src := &E3{
|
||||
F01: true,
|
||||
F02: 4.2,
|
||||
F03: 4.3,
|
||||
F04: -42,
|
||||
F05: -43,
|
||||
F06: -44,
|
||||
F07: -45,
|
||||
F08: -46,
|
||||
F09: "foo",
|
||||
F10: 42,
|
||||
F11: 43,
|
||||
F12: 44,
|
||||
F13: 45,
|
||||
F14: 46,
|
||||
F15: 1,
|
||||
}
|
||||
dst := &E3{}
|
||||
|
||||
vals := make(map[string][]string)
|
||||
encoder := NewEncoder()
|
||||
decoder := NewDecoder()
|
||||
|
||||
encoder.RegisterEncoder(src.F15, func(reflect.Value) string { return "1" })
|
||||
decoder.RegisterConverter(src.F15, func(string) reflect.Value { return reflect.ValueOf(1) })
|
||||
|
||||
err := encoder.Encode(src, vals)
|
||||
if err != nil {
|
||||
t.Errorf("Encoder has non-nil error: %v", err)
|
||||
}
|
||||
err = decoder.Decode(dst, vals)
|
||||
if err != nil {
|
||||
t.Errorf("Decoder has non-nil error: %v", err)
|
||||
}
|
||||
|
||||
if *src != *dst {
|
||||
t.Errorf("Decoder-Encoder compatibility: expected %v, got %v\n", src, dst)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
s := &E1{
|
||||
F01: 1,
|
||||
F02: 2,
|
||||
F03: "three",
|
||||
}
|
||||
|
||||
estr := "schema: encoder not found for <nil>"
|
||||
vals := make(map[string][]string)
|
||||
err := NewEncoder().Encode(s, vals)
|
||||
if err.Error() != estr {
|
||||
t.Errorf("Expected: %s, got %v", estr, err)
|
||||
}
|
||||
|
||||
valExists(t, "f03", "three", vals)
|
||||
valNotExists(t, "f04", vals)
|
||||
}
|
||||
|
||||
func TestStruct(t *testing.T) {
|
||||
estr := "schema: interface must be a struct"
|
||||
vals := make(map[string][]string)
|
||||
err := NewEncoder().Encode("hello world", vals)
|
||||
|
||||
if err.Error() != estr {
|
||||
t.Errorf("Expected: %s, got %v", estr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlices(t *testing.T) {
|
||||
type oneAsWord int
|
||||
ones := []oneAsWord{1, 2}
|
||||
s1 := &struct {
|
||||
ones []oneAsWord `schema:"ones"`
|
||||
ints []int `schema:"ints"`
|
||||
nonempty []int `schema:"nonempty"`
|
||||
empty []int `schema:"empty,omitempty"`
|
||||
}{ones, []int{1, 1}, []int{}, []int{}}
|
||||
vals := make(map[string][]string)
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
|
||||
err := encoder.Encode(s1, vals)
|
||||
if err != nil {
|
||||
t.Errorf("Encoder has non-nil error: %v", err)
|
||||
}
|
||||
|
||||
valsExist(t, "ones", []string{"one", "one"}, vals)
|
||||
valsExist(t, "ints", []string{"1", "1"}, vals)
|
||||
valsExist(t, "nonempty", []string{}, vals)
|
||||
valNotExists(t, "empty", vals)
|
||||
}
|
||||
|
||||
func TestCompatSlices(t *testing.T) {
|
||||
type oneAsWord int
|
||||
type s1 struct {
|
||||
Ones []oneAsWord `schema:"ones"`
|
||||
Ints []int `schema:"ints"`
|
||||
}
|
||||
ones := []oneAsWord{1, 1}
|
||||
src := &s1{ones, []int{1, 1}}
|
||||
vals := make(map[string][]string)
|
||||
dst := &s1{}
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.RegisterEncoder(ones[0], func(v reflect.Value) string { return "one" })
|
||||
|
||||
decoder := NewDecoder()
|
||||
decoder.RegisterConverter(ones[0], func(s string) reflect.Value {
|
||||
if s == "one" {
|
||||
return reflect.ValueOf(1)
|
||||
}
|
||||
return reflect.ValueOf(2)
|
||||
})
|
||||
|
||||
err := encoder.Encode(src, vals)
|
||||
if err != nil {
|
||||
t.Errorf("Encoder has non-nil error: %v", err)
|
||||
}
|
||||
err = decoder.Decode(dst, vals)
|
||||
if err != nil {
|
||||
t.Errorf("Dncoder has non-nil error: %v", err)
|
||||
}
|
||||
|
||||
if len(src.Ints) != len(dst.Ints) || len(src.Ones) != len(src.Ones) {
|
||||
t.Fatalf("Expected %v, got %v", src, dst)
|
||||
}
|
||||
|
||||
for i, v := range src.Ones {
|
||||
if dst.Ones[i] != v {
|
||||
t.Fatalf("Expected %v, got %v", v, dst.Ones[i])
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range src.Ints {
|
||||
if dst.Ints[i] != v {
|
||||
t.Fatalf("Expected %v, got %v", v, dst.Ints[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterEncoder(t *testing.T) {
|
||||
type oneAsWord int
|
||||
type twoAsWord int
|
||||
type oneSliceAsWord []int
|
||||
|
||||
s1 := &struct {
|
||||
oneAsWord
|
||||
twoAsWord
|
||||
oneSliceAsWord
|
||||
}{1, 2, []int{1, 1}}
|
||||
v1 := make(map[string][]string)
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.RegisterEncoder(s1.oneAsWord, func(v reflect.Value) string { return "one" })
|
||||
encoder.RegisterEncoder(s1.twoAsWord, func(v reflect.Value) string { return "two" })
|
||||
encoder.RegisterEncoder(s1.oneSliceAsWord, func(v reflect.Value) string { return "one" })
|
||||
|
||||
err := encoder.Encode(s1, v1)
|
||||
if err != nil {
|
||||
t.Errorf("Encoder has non-nil error: %v", err)
|
||||
}
|
||||
|
||||
valExists(t, "oneAsWord", "one", v1)
|
||||
valExists(t, "twoAsWord", "two", v1)
|
||||
valExists(t, "oneSliceAsWord", "one", v1)
|
||||
}
|
||||
|
||||
func TestEncoderOrder(t *testing.T) {
|
||||
type builtinEncoderSimple int
|
||||
type builtinEncoderSimpleOverridden int
|
||||
type builtinEncoderSlice []int
|
||||
type builtinEncoderSliceOverridden []int
|
||||
type builtinEncoderStruct struct{ nr int }
|
||||
type builtinEncoderStructOverridden struct{ nr int }
|
||||
|
||||
s1 := &struct {
|
||||
builtinEncoderSimple `schema:"simple"`
|
||||
builtinEncoderSimpleOverridden `schema:"simple_overridden"`
|
||||
builtinEncoderSlice `schema:"slice"`
|
||||
builtinEncoderSliceOverridden `schema:"slice_overridden"`
|
||||
builtinEncoderStruct `schema:"struct"`
|
||||
builtinEncoderStructOverridden `schema:"struct_overridden"`
|
||||
}{
|
||||
1,
|
||||
1,
|
||||
[]int{2},
|
||||
[]int{2},
|
||||
builtinEncoderStruct{3},
|
||||
builtinEncoderStructOverridden{3},
|
||||
}
|
||||
v1 := make(map[string][]string)
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.RegisterEncoder(s1.builtinEncoderSimpleOverridden, func(v reflect.Value) string { return "one" })
|
||||
encoder.RegisterEncoder(s1.builtinEncoderSliceOverridden, func(v reflect.Value) string { return "two" })
|
||||
encoder.RegisterEncoder(s1.builtinEncoderStructOverridden, func(v reflect.Value) string { return "three" })
|
||||
|
||||
err := encoder.Encode(s1, v1)
|
||||
if err != nil {
|
||||
t.Errorf("Encoder has non-nil error: %v", err)
|
||||
}
|
||||
|
||||
valExists(t, "simple", "1", v1)
|
||||
valExists(t, "simple_overridden", "one", v1)
|
||||
valExists(t, "slice", "2", v1)
|
||||
valExists(t, "slice_overridden", "two", v1)
|
||||
valExists(t, "nr", "3", v1)
|
||||
valExists(t, "struct_overridden", "three", v1)
|
||||
}
|
||||
|
||||
func valExists(t *testing.T, key string, expect string, result map[string][]string) {
|
||||
valsExist(t, key, []string{expect}, result)
|
||||
}
|
||||
|
||||
func valsExist(t *testing.T, key string, expect []string, result map[string][]string) {
|
||||
vals, ok := result[key]
|
||||
if !ok {
|
||||
t.Fatalf("Key not found. Expected: %s", key)
|
||||
}
|
||||
|
||||
if len(expect) != len(vals) {
|
||||
t.Fatalf("Expected: %v, got: %v", expect, vals)
|
||||
}
|
||||
|
||||
for i, v := range expect {
|
||||
if vals[i] != v {
|
||||
t.Fatalf("Unexpected value. Expected: %v, got %v", v, vals[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func valNotExists(t *testing.T, key string, result map[string][]string) {
|
||||
if val, ok := result[key]; ok {
|
||||
t.Error("Key not ommited. Expected: empty; got: " + val[0] + ".")
|
||||
}
|
||||
}
|
||||
|
||||
type E4 struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func TestEncoderSetAliasTag(t *testing.T) {
|
||||
data := map[string][]string{}
|
||||
|
||||
s := E4{
|
||||
ID: "foo",
|
||||
}
|
||||
encoder := NewEncoder()
|
||||
encoder.SetAliasTag("json")
|
||||
encoder.Encode(&s, data)
|
||||
valExists(t, "id", "foo", data)
|
||||
}
|
||||
|
||||
type E5 struct {
|
||||
F01 int `schema:"f01,omitempty"`
|
||||
F02 string `schema:"f02,omitempty"`
|
||||
F03 *string `schema:"f03,omitempty"`
|
||||
F04 *int8 `schema:"f04,omitempty"`
|
||||
F05 float64 `schema:"f05,omitempty"`
|
||||
F06 E5F06 `schema:"f06,omitempty"`
|
||||
F07 E5F06 `schema:"f07,omitempty"`
|
||||
F08 []string `schema:"f08,omitempty"`
|
||||
F09 []string `schema:"f09,omitempty"`
|
||||
}
|
||||
|
||||
type E5F06 struct {
|
||||
F0601 string `schema:"f0601,omitempty"`
|
||||
}
|
||||
|
||||
func TestEncoderWithOmitempty(t *testing.T) {
|
||||
vals := map[string][]string{}
|
||||
|
||||
s := E5{
|
||||
F02: "test",
|
||||
F07: E5F06{
|
||||
F0601: "test",
|
||||
},
|
||||
F09: []string{"test"},
|
||||
}
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.Encode(&s, vals)
|
||||
|
||||
valNotExists(t, "f01", vals)
|
||||
valExists(t, "f02", "test", vals)
|
||||
valNotExists(t, "f03", vals)
|
||||
valNotExists(t, "f04", vals)
|
||||
valNotExists(t, "f05", vals)
|
||||
valNotExists(t, "f06", vals)
|
||||
valExists(t, "f0601", "test", vals)
|
||||
valNotExists(t, "f08", vals)
|
||||
valsExist(t, "f09", []string{"test"}, vals)
|
||||
}
|
||||
|
||||
type E6 struct {
|
||||
F01 *inner
|
||||
F02 *inner
|
||||
F03 *inner `schema:",omitempty"`
|
||||
}
|
||||
|
||||
func TestStructPointer(t *testing.T) {
|
||||
vals := map[string][]string{}
|
||||
s := E6{
|
||||
F01: &inner{2},
|
||||
}
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.Encode(&s, vals)
|
||||
valExists(t, "F12", "2", vals)
|
||||
valExists(t, "F02", "null", vals)
|
||||
valNotExists(t, "F03", vals)
|
||||
}
|
||||
|
||||
func TestRegisterEncoderCustomArrayType(t *testing.T) {
|
||||
type CustomInt []int
|
||||
type S1 struct {
|
||||
SomeInts CustomInt `schema:",omitempty"`
|
||||
}
|
||||
|
||||
ss := []S1{
|
||||
{},
|
||||
{CustomInt{}},
|
||||
{CustomInt{1, 2, 3}},
|
||||
}
|
||||
|
||||
for s := range ss {
|
||||
vals := map[string][]string{}
|
||||
|
||||
encoder := NewEncoder()
|
||||
encoder.RegisterEncoder(CustomInt{}, func(value reflect.Value) string {
|
||||
return fmt.Sprint(value.Interface())
|
||||
})
|
||||
|
||||
encoder.Encode(s, vals)
|
||||
t.Log(vals)
|
||||
}
|
||||
}
|
||||
15
vendor/github.com/gorilla/websocket/conn.go
сгенерированный
поставляемый
15
vendor/github.com/gorilla/websocket/conn.go
сгенерированный
поставляемый
@@ -1051,8 +1051,9 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
|
||||
// if the close message is empty. The default close handler sends a close
|
||||
// message back to the peer.
|
||||
//
|
||||
// The application must read the connection to process close messages as
|
||||
// described in the section on Control Messages above.
|
||||
// The handler function is called from the NextReader, ReadMessage and message
|
||||
// reader Read methods. The application must read the connection to process
|
||||
// close messages as described in the section on Control Messages above.
|
||||
//
|
||||
// The connection read methods return a CloseError when a close message is
|
||||
// received. Most applications should handle close messages as part of their
|
||||
@@ -1079,8 +1080,9 @@ func (c *Conn) PingHandler() func(appData string) error {
|
||||
// The appData argument to h is the PING message application data. The default
|
||||
// ping handler sends a pong to the peer.
|
||||
//
|
||||
// The application must read the connection to process ping messages as
|
||||
// described in the section on Control Messages above.
|
||||
// The handler function is called from the NextReader, ReadMessage and message
|
||||
// reader Read methods. The application must read the connection to process
|
||||
// ping messages as described in the section on Control Messages above.
|
||||
func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
if h == nil {
|
||||
h = func(message string) error {
|
||||
@@ -1105,8 +1107,9 @@ func (c *Conn) PongHandler() func(appData string) error {
|
||||
// The appData argument to h is the PONG message application data. The default
|
||||
// pong handler does nothing.
|
||||
//
|
||||
// The application must read the connection to process ping messages as
|
||||
// described in the section on Control Messages above.
|
||||
// The handler function is called from the NextReader, ReadMessage and message
|
||||
// reader Read methods. The application must read the connection to process
|
||||
// pong messages as described in the section on Control Messages above.
|
||||
func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||
if h == nil {
|
||||
h = func(string) error { return nil }
|
||||
|
||||
7
vendor/github.com/hashicorp/go-immutable-radix/iradix.go
сгенерированный
поставляемый
7
vendor/github.com/hashicorp/go-immutable-radix/iradix.go
сгенерированный
поставляемый
@@ -338,6 +338,11 @@ func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
|
||||
if !n.isLeaf() {
|
||||
return nil, nil
|
||||
}
|
||||
// Copy the pointer in case we are in a transaction that already
|
||||
// modified this node since the node will be reused. Any changes
|
||||
// made to the node will not affect returning the original leaf
|
||||
// value.
|
||||
oldLeaf := n.leaf
|
||||
|
||||
// Remove the leaf node
|
||||
nc := t.writeNode(n, true)
|
||||
@@ -347,7 +352,7 @@ func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
|
||||
if n != t.root && len(nc.edges) == 1 {
|
||||
t.mergeChild(nc)
|
||||
}
|
||||
return nc, n.leaf
|
||||
return nc, oldLeaf
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
|
||||
37
vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go
сгенерированный
поставляемый
37
vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go
сгенерированный
поставляемый
@@ -173,7 +173,7 @@ func TestRoot(t *testing.T) {
|
||||
}
|
||||
val, ok := r.Get(nil)
|
||||
if !ok || val != true {
|
||||
t.Fatalf("bad: %v %#v", val)
|
||||
t.Fatalf("bad: %#v", val)
|
||||
}
|
||||
r, val, ok = r.Delete(nil)
|
||||
if !ok || val != true {
|
||||
@@ -1494,3 +1494,38 @@ func TestTrackMutate_cachedNodeChange(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLenTxn(t *testing.T) {
|
||||
r := New()
|
||||
|
||||
if r.Len() != 0 {
|
||||
t.Fatalf("not starting with empty tree")
|
||||
}
|
||||
|
||||
txn := r.Txn()
|
||||
keys := []string{
|
||||
"foo/bar/baz",
|
||||
"foo/baz/bar",
|
||||
"foo/zip/zap",
|
||||
"foobar",
|
||||
"nochange",
|
||||
}
|
||||
for _, k := range keys {
|
||||
txn.Insert([]byte(k), nil)
|
||||
}
|
||||
r = txn.Commit()
|
||||
|
||||
if r.Len() != len(keys) {
|
||||
t.Fatalf("bad: expected %d, got %d", len(keys), r.Len())
|
||||
}
|
||||
|
||||
txn = r.Txn()
|
||||
for _, k := range keys {
|
||||
txn.Delete([]byte(k))
|
||||
}
|
||||
r = txn.Commit()
|
||||
|
||||
if r.Len() != 0 {
|
||||
t.Fatalf("tree len should be zero, got %d", r.Len())
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
сгенерированный
поставляемый
2
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
сгенерированный
поставляемый
@@ -16,7 +16,7 @@ var (
|
||||
// Centralize all regexps and regexp.Copy() where necessary.
|
||||
signRE *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`)
|
||||
whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`)
|
||||
ifNameRE *regexp.Regexp = regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
|
||||
ifNameRE *regexp.Regexp = regexp.MustCompile(`^Ethernet adapter ([^:]+):`)
|
||||
ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
|
||||
)
|
||||
|
||||
|
||||
21
vendor/github.com/hashicorp/golang-lru/2q.go
сгенерированный
поставляемый
21
vendor/github.com/hashicorp/golang-lru/2q.go
сгенерированный
поставляемый
@@ -30,9 +30,9 @@ type TwoQueueCache struct {
|
||||
size int
|
||||
recentSize int
|
||||
|
||||
recent *simplelru.LRU
|
||||
frequent *simplelru.LRU
|
||||
recentEvict *simplelru.LRU
|
||||
recent simplelru.LRUCache
|
||||
frequent simplelru.LRUCache
|
||||
recentEvict simplelru.LRUCache
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCa
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
@@ -105,6 +106,7 @@ func (c *TwoQueueCache) Get(key interface{}) (interface{}, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Add adds a value to the cache.
|
||||
func (c *TwoQueueCache) Add(key, value interface{}) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@@ -160,12 +162,15 @@ func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
|
||||
c.frequent.RemoveOldest()
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *TwoQueueCache) Len() int {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.recent.Len() + c.frequent.Len()
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache.
|
||||
// The frequently used keys are first in the returned slice.
|
||||
func (c *TwoQueueCache) Keys() []interface{} {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
@@ -174,6 +179,7 @@ func (c *TwoQueueCache) Keys() []interface{} {
|
||||
return append(k1, k2...)
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *TwoQueueCache) Remove(key interface{}) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@@ -188,6 +194,7 @@ func (c *TwoQueueCache) Remove(key interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache.
|
||||
func (c *TwoQueueCache) Purge() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
@@ -196,13 +203,17 @@ func (c *TwoQueueCache) Purge() {
|
||||
c.recentEvict.Purge()
|
||||
}
|
||||
|
||||
// Contains is used to check if the cache contains a key
|
||||
// without updating recency or frequency.
|
||||
func (c *TwoQueueCache) Contains(key interface{}) bool {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.frequent.Contains(key) || c.recent.Contains(key)
|
||||
}
|
||||
|
||||
func (c *TwoQueueCache) Peek(key interface{}) (interface{}, bool) {
|
||||
// Peek is used to inspect the cache value of a key
|
||||
// without updating recency or frequency.
|
||||
func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
if val, ok := c.frequent.Peek(key); ok {
|
||||
|
||||
16
vendor/github.com/hashicorp/golang-lru/arc.go
сгенерированный
поставляемый
16
vendor/github.com/hashicorp/golang-lru/arc.go
сгенерированный
поставляемый
@@ -18,11 +18,11 @@ type ARCCache struct {
|
||||
size int // Size is the total capacity of the cache
|
||||
p int // P is the dynamic preference towards T1 or T2
|
||||
|
||||
t1 *simplelru.LRU // T1 is the LRU for recently accessed items
|
||||
b1 *simplelru.LRU // B1 is the LRU for evictions from t1
|
||||
t1 simplelru.LRUCache // T1 is the LRU for recently accessed items
|
||||
b1 simplelru.LRUCache // B1 is the LRU for evictions from t1
|
||||
|
||||
t2 *simplelru.LRU // T2 is the LRU for frequently accessed items
|
||||
b2 *simplelru.LRU // B2 is the LRU for evictions from t2
|
||||
t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items
|
||||
b2 simplelru.LRUCache // B2 is the LRU for evictions from t2
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
@@ -60,11 +60,11 @@ func NewARC(size int) (*ARCCache, error) {
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *ARCCache) Get(key interface{}) (interface{}, bool) {
|
||||
func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
// Ff the value is contained in T1 (recent), then
|
||||
// If the value is contained in T1 (recent), then
|
||||
// promote it to T2 (frequent)
|
||||
if val, ok := c.t1.Peek(key); ok {
|
||||
c.t1.Remove(key)
|
||||
@@ -153,7 +153,7 @@ func (c *ARCCache) Add(key, value interface{}) {
|
||||
// Remove from B2
|
||||
c.b2.Remove(key)
|
||||
|
||||
// Add the key to the frequntly used list
|
||||
// Add the key to the frequently used list
|
||||
c.t2.Add(key, value)
|
||||
return
|
||||
}
|
||||
@@ -247,7 +247,7 @@ func (c *ARCCache) Contains(key interface{}) bool {
|
||||
|
||||
// Peek is used to inspect the cache value of a key
|
||||
// without updating recency or frequency.
|
||||
func (c *ARCCache) Peek(key interface{}) (interface{}, bool) {
|
||||
func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
if val, ok := c.t1.Peek(key); ok {
|
||||
|
||||
21
vendor/github.com/hashicorp/golang-lru/doc.go
сгенерированный
поставляемый
Обычный файл
21
vendor/github.com/hashicorp/golang-lru/doc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
// Package lru provides three different LRU caches of varying sophistication.
|
||||
//
|
||||
// Cache is a simple LRU cache. It is based on the
|
||||
// LRU implementation in groupcache:
|
||||
// https://github.com/golang/groupcache/tree/master/lru
|
||||
//
|
||||
// TwoQueueCache tracks frequently used and recently used entries separately.
|
||||
// This avoids a burst of accesses from taking out frequently used entries,
|
||||
// at the cost of about 2x computational overhead and some extra bookkeeping.
|
||||
//
|
||||
// ARCCache is an adaptive replacement cache. It tracks recent evictions as
|
||||
// well as recent usage in both the frequent and recent caches. Its
|
||||
// computational overhead is comparable to TwoQueueCache, but the memory
|
||||
// overhead is linear with the size of the cache.
|
||||
//
|
||||
// ARC has been patented by IBM, so do not use it if that is problematic for
|
||||
// your program.
|
||||
//
|
||||
// All caches in this package take locks while operating, and are therefore
|
||||
// thread-safe for consumers.
|
||||
package lru
|
||||
28
vendor/github.com/hashicorp/golang-lru/lru.go
сгенерированный
поставляемый
28
vendor/github.com/hashicorp/golang-lru/lru.go
сгенерированный
поставляемый
@@ -1,6 +1,3 @@
|
||||
// This package provides a simple LRU cache. It is based on the
|
||||
// LRU implementation in groupcache:
|
||||
// https://github.com/golang/groupcache/tree/master/lru
|
||||
package lru
|
||||
|
||||
import (
|
||||
@@ -11,11 +8,11 @@ import (
|
||||
|
||||
// Cache is a thread-safe fixed size LRU cache.
|
||||
type Cache struct {
|
||||
lru *simplelru.LRU
|
||||
lru simplelru.LRUCache
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// New creates an LRU of the given size
|
||||
// New creates an LRU of the given size.
|
||||
func New(size int) (*Cache, error) {
|
||||
return NewWithEvict(size, nil)
|
||||
}
|
||||
@@ -33,7 +30,7 @@ func NewWithEvict(size int, onEvicted func(key interface{}, value interface{}))
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache
|
||||
// Purge is used to completely clear the cache.
|
||||
func (c *Cache) Purge() {
|
||||
c.lock.Lock()
|
||||
c.lru.Purge()
|
||||
@@ -41,30 +38,30 @@ func (c *Cache) Purge() {
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
func (c *Cache) Add(key, value interface{}) bool {
|
||||
func (c *Cache) Add(key, value interface{}) (evicted bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.lru.Add(key, value)
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *Cache) Get(key interface{}) (interface{}, bool) {
|
||||
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
return c.lru.Get(key)
|
||||
}
|
||||
|
||||
// Check if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
// Contains checks if a key is in the cache, without updating the
|
||||
// recent-ness or deleting it for being stale.
|
||||
func (c *Cache) Contains(key interface{}) bool {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.lru.Contains(key)
|
||||
}
|
||||
|
||||
// Returns the key value (or undefined if not found) without updating
|
||||
// Peek returns the key value (or undefined if not found) without updating
|
||||
// the "recently used"-ness of the key.
|
||||
func (c *Cache) Peek(key interface{}) (interface{}, bool) {
|
||||
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return c.lru.Peek(key)
|
||||
@@ -73,16 +70,15 @@ func (c *Cache) Peek(key interface{}) (interface{}, bool) {
|
||||
// ContainsOrAdd checks if a key is in the cache without updating the
|
||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
||||
// Returns whether found and whether an eviction occurred.
|
||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evict bool) {
|
||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if c.lru.Contains(key) {
|
||||
return true, false
|
||||
} else {
|
||||
evict := c.lru.Add(key, value)
|
||||
return false, evict
|
||||
}
|
||||
evicted = c.lru.Add(key, value)
|
||||
return false, evicted
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
|
||||
4
vendor/github.com/hashicorp/golang-lru/lru_test.go
сгенерированный
поставляемый
4
vendor/github.com/hashicorp/golang-lru/lru_test.go
сгенерированный
поставляемый
@@ -72,7 +72,7 @@ func TestLRU(t *testing.T) {
|
||||
if k != v {
|
||||
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
|
||||
}
|
||||
evictCounter += 1
|
||||
evictCounter++
|
||||
}
|
||||
l, err := NewWithEvict(128, onEvicted)
|
||||
if err != nil {
|
||||
@@ -136,7 +136,7 @@ func TestLRU(t *testing.T) {
|
||||
func TestLRUAdd(t *testing.T) {
|
||||
evictCounter := 0
|
||||
onEvicted := func(k interface{}, v interface{}) {
|
||||
evictCounter += 1
|
||||
evictCounter++
|
||||
}
|
||||
|
||||
l, err := NewWithEvict(1, onEvicted)
|
||||
|
||||
17
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
сгенерированный
поставляемый
17
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
сгенерированный
поставляемый
@@ -36,7 +36,7 @@ func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache
|
||||
// Purge is used to completely clear the cache.
|
||||
func (c *LRU) Purge() {
|
||||
for k, v := range c.items {
|
||||
if c.onEvict != nil {
|
||||
@@ -48,7 +48,7 @@ func (c *LRU) Purge() {
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
func (c *LRU) Add(key, value interface{}) bool {
|
||||
func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
@@ -78,17 +78,18 @@ func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if a key is in the cache, without updating the recent-ness
|
||||
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
func (c *LRU) Contains(key interface{}) (ok bool) {
|
||||
_, ok = c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Returns the key value (or undefined if not found) without updating
|
||||
// Peek returns the key value (or undefined if not found) without updating
|
||||
// the "recently used"-ness of the key.
|
||||
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
var ent *list.Element
|
||||
if ent, ok = c.items[key]; ok {
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return nil, ok
|
||||
@@ -96,7 +97,7 @@ func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
|
||||
// Remove removes the provided key from the cache, returning if the
|
||||
// key was contained.
|
||||
func (c *LRU) Remove(key interface{}) bool {
|
||||
func (c *LRU) Remove(key interface{}) (present bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
@@ -105,7 +106,7 @@ func (c *LRU) Remove(key interface{}) bool {
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
|
||||
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
@@ -116,7 +117,7 @@ func (c *LRU) RemoveOldest() (interface{}, interface{}, bool) {
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *LRU) GetOldest() (interface{}, interface{}, bool) {
|
||||
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
kv := ent.Value.(*entry)
|
||||
|
||||
37
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
сгенерированный
поставляемый
Обычный файл
37
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,37 @@
|
||||
package simplelru
|
||||
|
||||
|
||||
// LRUCache is the interface for simple LRU cache.
|
||||
type LRUCache interface {
|
||||
// Adds a value to the cache, returns true if an eviction occurred and
|
||||
// updates the "recently used"-ness of the key.
|
||||
Add(key, value interface{}) bool
|
||||
|
||||
// Returns key's value from the cache and
|
||||
// updates the "recently used"-ness of the key. #value, isFound
|
||||
Get(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Check if a key exsists in cache without updating the recent-ness.
|
||||
Contains(key interface{}) (ok bool)
|
||||
|
||||
// Returns key's value without updating the "recently used"-ness of the key.
|
||||
Peek(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Removes a key from the cache.
|
||||
Remove(key interface{}) bool
|
||||
|
||||
// Removes the oldest entry from cache.
|
||||
RemoveOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns the oldest entry from the cache. #key, value, isFound
|
||||
GetOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||
Keys() []interface{}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
Len() int
|
||||
|
||||
// Clear all cache entries
|
||||
Purge()
|
||||
}
|
||||
4
vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
сгенерированный
поставляемый
4
vendor/github.com/hashicorp/golang-lru/simplelru/lru_test.go
сгенерированный
поставляемый
@@ -8,7 +8,7 @@ func TestLRU(t *testing.T) {
|
||||
if k != v {
|
||||
t.Fatalf("Evict values not equal (%v!=%v)", k, v)
|
||||
}
|
||||
evictCounter += 1
|
||||
evictCounter++
|
||||
}
|
||||
l, err := NewLRU(128, onEvicted)
|
||||
if err != nil {
|
||||
@@ -112,7 +112,7 @@ func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
|
||||
func TestLRU_Add(t *testing.T) {
|
||||
evictCounter := 0
|
||||
onEvicted := func(k interface{}, v interface{}) {
|
||||
evictCounter += 1
|
||||
evictCounter++
|
||||
}
|
||||
|
||||
l, err := NewLRU(1, onEvicted)
|
||||
|
||||
2
vendor/github.com/hashicorp/memberlist/README.md
сгенерированный
поставляемый
2
vendor/github.com/hashicorp/memberlist/README.md
сгенерированный
поставляемый
@@ -65,7 +65,7 @@ For complete documentation, see the associated [Godoc](http://godoc.org/github.c
|
||||
|
||||
## Protocol
|
||||
|
||||
memberlist is based on ["SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"](http://www.cs.cornell.edu/~asdas/research/dsn02-swim.pdf). However, we extend the protocol in a number of ways:
|
||||
memberlist is based on ["SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"](http://ieeexplore.ieee.org/document/1028914/). However, we extend the protocol in a number of ways:
|
||||
|
||||
* Several extensions are made to increase propagation speed and
|
||||
convergence rate.
|
||||
|
||||
1
vendor/github.com/lib/pq/.travis.yml
сгенерированный
поставляемый
1
vendor/github.com/lib/pq/.travis.yml
сгенерированный
поставляемый
@@ -1,7 +1,6 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
|
||||
21
vendor/github.com/magiconair/properties/CHANGELOG.md
сгенерированный
поставляемый
21
vendor/github.com/magiconair/properties/CHANGELOG.md
сгенерированный
поставляемый
@@ -1,10 +1,29 @@
|
||||
## Changelog
|
||||
|
||||
### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018
|
||||
|
||||
* [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases.
|
||||
|
||||
See PR for an example.
|
||||
|
||||
Thanks to [@yobert](https://github.com/yobert) for the fix.
|
||||
|
||||
### [1.7.5](https://github.com/magiconair/properties/tree/v1.7.5) - 13 Feb 2018
|
||||
|
||||
* [PR #28](https://github.com/magiconair/properties/pull/28): Support duplicate expansions in the same value
|
||||
|
||||
Values which expand the same key multiple times (e.g. `key=${a} ${a}`) will no longer fail
|
||||
with a `circular reference error`.
|
||||
|
||||
Thanks to [@yobert](https://github.com/yobert) for the fix.
|
||||
|
||||
### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017
|
||||
|
||||
* [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces
|
||||
|
||||
* [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled
|
||||
Thanks to @mgurov for the fix.
|
||||
|
||||
Thanks to [@mgurov](https://github.com/mgurov) for the fix.
|
||||
|
||||
### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017
|
||||
|
||||
|
||||
2
vendor/github.com/magiconair/properties/LICENSE
сгенерированный
поставляемый
2
vendor/github.com/magiconair/properties/LICENSE
сгенерированный
поставляемый
@@ -1,6 +1,6 @@
|
||||
goproperties - properties file decoder for Go
|
||||
|
||||
Copyright (c) 2013-2014 - Frank Schroeder
|
||||
Copyright (c) 2013-2018 - Frank Schroeder
|
||||
|
||||
All rights reserved.
|
||||
|
||||
|
||||
50
vendor/github.com/magiconair/properties/README.md
сгенерированный
поставляемый
50
vendor/github.com/magiconair/properties/README.md
сгенерированный
поставляемый
@@ -1,7 +1,11 @@
|
||||
Overview [](https://travis-ci.org/magiconair/properties)
|
||||
========
|
||||
[](https://github.com/magiconair/properties/releases)
|
||||
[](https://travis-ci.org/magiconair/properties)
|
||||
[](https://raw.githubusercontent.com/magiconair/properties/master/LICENSE)
|
||||
[](http://godoc.org/github.com/magiconair/properties)
|
||||
|
||||
#### Current version: 1.7.4
|
||||
# Overview
|
||||
|
||||
#### Please run `git pull --tags` to update the tags. See [below](#updated-git-tags) why.
|
||||
|
||||
properties is a Go library for reading and writing properties files.
|
||||
|
||||
@@ -27,8 +31,7 @@ details.
|
||||
|
||||
Read the full documentation on [GoDoc](https://godoc.org/github.com/magiconair/properties) [](https://godoc.org/github.com/magiconair/properties)
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
## Getting Started
|
||||
|
||||
```go
|
||||
import (
|
||||
@@ -83,18 +86,43 @@ func main() {
|
||||
|
||||
```
|
||||
|
||||
Installation and Upgrade
|
||||
------------------------
|
||||
## Installation and Upgrade
|
||||
|
||||
```
|
||||
$ go get -u github.com/magiconair/properties
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
## License
|
||||
|
||||
2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details.
|
||||
|
||||
ToDo
|
||||
----
|
||||
## ToDo
|
||||
|
||||
* Dump contents with passwords and secrets obscured
|
||||
|
||||
## Updated Git tags
|
||||
|
||||
#### 13 Feb 2018
|
||||
|
||||
I realized that all of the git tags I had pushed before v1.7.5 were lightweight tags
|
||||
and I've only recently learned that this doesn't play well with `git describe` 😞
|
||||
|
||||
I have replaced all lightweight tags with signed tags using this script which should
|
||||
retain the commit date, name and email address. Please run `git pull --tags` to update them.
|
||||
|
||||
Worst case you have to reclone the repo.
|
||||
|
||||
```shell
|
||||
#!/bin/bash
|
||||
tag=$1
|
||||
echo "Updating $tag"
|
||||
date=$(git show ${tag}^0 --format=%aD | head -1)
|
||||
email=$(git show ${tag}^0 --format=%aE | head -1)
|
||||
name=$(git show ${tag}^0 --format=%aN | head -1)
|
||||
GIT_COMMITTER_DATE="$date" GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" git tag -s -f ${tag} ${tag}^0 -m ${tag}
|
||||
```
|
||||
|
||||
I apologize for the inconvenience.
|
||||
|
||||
Frank
|
||||
|
||||
|
||||
2
vendor/github.com/magiconair/properties/load.go
сгенерированный
поставляемый
2
vendor/github.com/magiconair/properties/load.go
сгенерированный
поставляемый
@@ -218,7 +218,7 @@ func must(p *Properties, err error) *Properties {
|
||||
// with an empty string. Malformed expressions like "${ENV_VAR" will
|
||||
// be reported as error.
|
||||
func expandName(name string) (string, error) {
|
||||
return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
|
||||
return expand(name, []string{}, "${", "}", make(map[string]string))
|
||||
}
|
||||
|
||||
// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
|
||||
|
||||
77
vendor/github.com/magiconair/properties/properties.go
сгенерированный
поставляемый
77
vendor/github.com/magiconair/properties/properties.go
сгенерированный
поставляемый
@@ -19,6 +19,8 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const maxExpansionDepth = 64
|
||||
|
||||
// ErrorHandlerFunc defines the type of function which handles failures
|
||||
// of the MustXXX() functions. An error handler function must exit
|
||||
// the application after handling the error.
|
||||
@@ -92,7 +94,7 @@ func (p *Properties) Get(key string) (value string, ok bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
expanded, err := p.expand(v)
|
||||
expanded, err := p.expand(key, v)
|
||||
|
||||
// we guarantee that the expanded value is free of
|
||||
// circular references and malformed expressions
|
||||
@@ -525,7 +527,7 @@ func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
|
||||
p.m[key] = value
|
||||
|
||||
// now check for a circular reference
|
||||
_, err = p.expand(value)
|
||||
_, err = p.expand(key, value)
|
||||
if err != nil {
|
||||
|
||||
// revert to the previous state
|
||||
@@ -696,56 +698,65 @@ outer:
|
||||
// check expands all values and returns an error if a circular reference or
|
||||
// a malformed expression was found.
|
||||
func (p *Properties) check() error {
|
||||
for _, value := range p.m {
|
||||
if _, err := p.expand(value); err != nil {
|
||||
for key, value := range p.m {
|
||||
if _, err := p.expand(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Properties) expand(input string) (string, error) {
|
||||
func (p *Properties) expand(key, input string) (string, error) {
|
||||
// no pre/postfix -> nothing to expand
|
||||
if p.Prefix == "" && p.Postfix == "" {
|
||||
return input, nil
|
||||
}
|
||||
|
||||
return expand(input, make(map[string]bool), p.Prefix, p.Postfix, p.m)
|
||||
return expand(input, []string{key}, p.Prefix, p.Postfix, p.m)
|
||||
}
|
||||
|
||||
// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
|
||||
// The function keeps track of the keys that were already expanded and stops if it
|
||||
// detects a circular reference or a malformed expression of the form '(prefix)key'.
|
||||
func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) {
|
||||
start := strings.Index(s, prefix)
|
||||
if start == -1 {
|
||||
return s, nil
|
||||
func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) {
|
||||
if len(keys) > maxExpansionDepth {
|
||||
return "", fmt.Errorf("expansion too deep")
|
||||
}
|
||||
|
||||
keyStart := start + len(prefix)
|
||||
keyLen := strings.Index(s[keyStart:], postfix)
|
||||
if keyLen == -1 {
|
||||
return "", fmt.Errorf("malformed expression")
|
||||
for {
|
||||
start := strings.Index(s, prefix)
|
||||
if start == -1 {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
keyStart := start + len(prefix)
|
||||
keyLen := strings.Index(s[keyStart:], postfix)
|
||||
if keyLen == -1 {
|
||||
return "", fmt.Errorf("malformed expression")
|
||||
}
|
||||
|
||||
end := keyStart + keyLen + len(postfix) - 1
|
||||
key := s[keyStart : keyStart+keyLen]
|
||||
|
||||
// fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
|
||||
|
||||
for _, k := range keys {
|
||||
if key == k {
|
||||
return "", fmt.Errorf("circular reference")
|
||||
}
|
||||
}
|
||||
|
||||
val, ok := values[key]
|
||||
if !ok {
|
||||
val = os.Getenv(key)
|
||||
}
|
||||
new_val, err := expand(val, append(keys, key), prefix, postfix, values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s = s[:start] + new_val + s[end+1:]
|
||||
}
|
||||
|
||||
end := keyStart + keyLen + len(postfix) - 1
|
||||
key := s[keyStart : keyStart+keyLen]
|
||||
|
||||
// fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key)
|
||||
|
||||
if _, ok := keys[key]; ok {
|
||||
return "", fmt.Errorf("circular reference")
|
||||
}
|
||||
|
||||
val, ok := values[key]
|
||||
if !ok {
|
||||
val = os.Getenv(key)
|
||||
}
|
||||
|
||||
// remember that we've seen the key
|
||||
keys[key] = true
|
||||
|
||||
return expand(s[:start]+val+s[end+1:], keys, prefix, postfix, values)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
|
||||
|
||||
28
vendor/github.com/magiconair/properties/properties_test.go
сгенерированный
поставляемый
28
vendor/github.com/magiconair/properties/properties_test.go
сгенерированный
поставляемый
@@ -90,6 +90,10 @@ var complexTests = [][]string{
|
||||
{"key=value\nkey2=${key}bb", "key", "value", "key2", "valuebb"},
|
||||
{"key=value\nkey2=aa${key}bb", "key", "value", "key2", "aavaluebb"},
|
||||
{"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"},
|
||||
{"key=value\nkey2=${key}${key}", "key", "value", "key2", "valuevalue"},
|
||||
{"key=value\nkey2=${key}${key}${key}${key}", "key", "value", "key2", "valuevaluevaluevalue"},
|
||||
{"key=value\nkey2=${key}${key3}\nkey3=${key}", "key", "value", "key2", "valuevalue", "key3", "value"},
|
||||
{"key=value\nkey2=${key3}${key}${key4}\nkey3=${key}\nkey4=${key}", "key", "value", "key2", "valuevaluevalue", "key3", "value", "key4", "value"},
|
||||
{"key=${USER}", "key", os.Getenv("USER")},
|
||||
{"key=${USER}\nUSER=value", "key", "value", "USER", "value"},
|
||||
}
|
||||
@@ -446,6 +450,30 @@ func TestErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVeryDeep(t *testing.T) {
|
||||
input := "key0=value\n"
|
||||
prefix := "${"
|
||||
postfix := "}"
|
||||
i := 0
|
||||
for i = 0; i < maxExpansionDepth-1; i++ {
|
||||
input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
|
||||
}
|
||||
|
||||
p, err := Load([]byte(input), ISO_8859_1)
|
||||
assert.Equal(t, err, nil)
|
||||
p.Prefix = prefix
|
||||
p.Postfix = postfix
|
||||
|
||||
assert.Equal(t, p.MustGet(fmt.Sprintf("key%d", i)), "value")
|
||||
|
||||
// Nudge input over the edge
|
||||
input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
|
||||
|
||||
_, err = Load([]byte(input), ISO_8859_1)
|
||||
assert.Equal(t, err != nil, true, "want error")
|
||||
assert.Equal(t, strings.Contains(err.Error(), "expansion too deep"), true)
|
||||
}
|
||||
|
||||
func TestDisableExpansion(t *testing.T) {
|
||||
input := "key=value\nkey2=${key}"
|
||||
p := mustParse(t, input)
|
||||
|
||||
4
vendor/github.com/minio/minio-go/api-compose-object.go
сгенерированный
поставляемый
4
vendor/github.com/minio/minio-go/api-compose-object.go
сгенерированный
поставляемый
@@ -476,8 +476,8 @@ func (c Client) ComposeObject(dst DestinationInfo, srcs []SourceInfo) error {
|
||||
|
||||
// Single source object case (i.e. when only one source is
|
||||
// involved, it is being copied wholly and at most 5GiB in
|
||||
// size).
|
||||
if totalParts == 1 && srcs[0].start == -1 && totalSize <= maxPartSize {
|
||||
// size, emptyfiles are also supported).
|
||||
if (totalParts == 1 && srcs[0].start == -1 && totalSize <= maxPartSize) || (totalSize == 0) {
|
||||
h := srcs[0].Headers
|
||||
// Add destination encryption headers
|
||||
for k, v := range dst.encryption.getSSEHeaders(false) {
|
||||
|
||||
61
vendor/github.com/minio/minio-go/api.go
сгенерированный
поставляемый
61
vendor/github.com/minio/minio-go/api.go
сгенерированный
поставляемый
@@ -81,12 +81,25 @@ type Client struct {
|
||||
|
||||
// Random seed.
|
||||
random *rand.Rand
|
||||
|
||||
// lookup indicates type of url lookup supported by server. If not specified,
|
||||
// default to Auto.
|
||||
lookup BucketLookupType
|
||||
}
|
||||
|
||||
// Options for New method
|
||||
type Options struct {
|
||||
Creds *credentials.Credentials
|
||||
Secure bool
|
||||
Region string
|
||||
BucketLookup BucketLookupType
|
||||
// Add future fields here
|
||||
}
|
||||
|
||||
// Global constants.
|
||||
const (
|
||||
libraryName = "minio-go"
|
||||
libraryVersion = "4.0.6"
|
||||
libraryVersion = "4.0.7"
|
||||
)
|
||||
|
||||
// User Agent should always following the below style.
|
||||
@@ -98,11 +111,21 @@ const (
|
||||
libraryUserAgent = libraryUserAgentPrefix + libraryName + "/" + libraryVersion
|
||||
)
|
||||
|
||||
// BucketLookupType is type of url lookup supported by server.
|
||||
type BucketLookupType int
|
||||
|
||||
// Different types of url lookup supported by the server.Initialized to BucketLookupAuto
|
||||
const (
|
||||
BucketLookupAuto BucketLookupType = iota
|
||||
BucketLookupDNS
|
||||
BucketLookupPath
|
||||
)
|
||||
|
||||
// NewV2 - instantiate minio client with Amazon S3 signature version
|
||||
// '2' compatibility.
|
||||
func NewV2(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
|
||||
creds := credentials.NewStaticV2(accessKeyID, secretAccessKey, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -114,7 +137,7 @@ func NewV2(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*
|
||||
// '4' compatibility.
|
||||
func NewV4(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
|
||||
creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -125,7 +148,7 @@ func NewV4(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*
|
||||
// New - instantiate minio client, adds automatic verification of signature.
|
||||
func New(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
|
||||
creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "")
|
||||
clnt, err := privateNew(endpoint, creds, secure, "", BucketLookupAuto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,7 +167,7 @@ func New(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Client, e
|
||||
// for retrieving credentials from various credentials provider such as
|
||||
// IAM, File, Env etc.
|
||||
func NewWithCredentials(endpoint string, creds *credentials.Credentials, secure bool, region string) (*Client, error) {
|
||||
return privateNew(endpoint, creds, secure, region)
|
||||
return privateNew(endpoint, creds, secure, region, BucketLookupAuto)
|
||||
}
|
||||
|
||||
// NewWithRegion - instantiate minio client, with region configured. Unlike New(),
|
||||
@@ -152,7 +175,12 @@ func NewWithCredentials(endpoint string, creds *credentials.Credentials, secure
|
||||
// Use this function when if your application deals with single region.
|
||||
func NewWithRegion(endpoint, accessKeyID, secretAccessKey string, secure bool, region string) (*Client, error) {
|
||||
creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
|
||||
return privateNew(endpoint, creds, secure, region)
|
||||
return privateNew(endpoint, creds, secure, region, BucketLookupAuto)
|
||||
}
|
||||
|
||||
// NewWithOptions - instantiate minio client with options
|
||||
func NewWithOptions(endpoint string, opts *Options) (*Client, error) {
|
||||
return privateNew(endpoint, opts.Creds, opts.Secure, opts.Region, opts.BucketLookup)
|
||||
}
|
||||
|
||||
// lockedRandSource provides protected rand source, implements rand.Source interface.
|
||||
@@ -239,7 +267,7 @@ func (c *Client) redirectHeaders(req *http.Request, via []*http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func privateNew(endpoint string, creds *credentials.Credentials, secure bool, region string) (*Client, error) {
|
||||
func privateNew(endpoint string, creds *credentials.Credentials, secure bool, region string, lookup BucketLookupType) (*Client, error) {
|
||||
// construct endpoint.
|
||||
endpointURL, err := getEndpointURL(endpoint, secure)
|
||||
if err != nil {
|
||||
@@ -276,6 +304,9 @@ func privateNew(endpoint string, creds *credentials.Credentials, secure bool, re
|
||||
// Introduce a new locked random seed.
|
||||
clnt.random = rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())})
|
||||
|
||||
// Sets bucket lookup style, whether server accepts DNS or Path lookup. Default is Auto - determined
|
||||
// by the SDK. When Auto is specified, DNS lookup is used for Amazon/Google cloud endpoints and Path for all other endpoints.
|
||||
clnt.lookup = lookup
|
||||
// Return.
|
||||
return clnt, nil
|
||||
}
|
||||
@@ -824,8 +855,7 @@ func (c Client) makeTargetURL(bucketName, objectName, bucketLocation string, que
|
||||
// endpoint URL.
|
||||
if bucketName != "" {
|
||||
// Save if target url will have buckets which suppport virtual host.
|
||||
isVirtualHostStyle := s3utils.IsVirtualHostSupported(*c.endpointURL, bucketName)
|
||||
|
||||
isVirtualHostStyle := c.isVirtualHostStyleRequest(*c.endpointURL, bucketName)
|
||||
// If endpoint supports virtual host style use that always.
|
||||
// Currently only S3 and Google Cloud Storage would support
|
||||
// virtual host style.
|
||||
@@ -850,3 +880,16 @@ func (c Client) makeTargetURL(bucketName, objectName, bucketLocation string, que
|
||||
|
||||
return url.Parse(urlStr)
|
||||
}
|
||||
|
||||
// returns true if virtual hosted style requests are to be used.
|
||||
func (c *Client) isVirtualHostStyleRequest(url url.URL, bucketName string) bool {
|
||||
if c.lookup == BucketLookupDNS {
|
||||
return true
|
||||
}
|
||||
if c.lookup == BucketLookupPath {
|
||||
return false
|
||||
}
|
||||
// default to virtual only for Amazon/Google storage. In all other cases use
|
||||
// path style requests
|
||||
return s3utils.IsVirtualHostSupported(url, bucketName)
|
||||
}
|
||||
|
||||
19
vendor/github.com/minio/minio-go/docs/API.md
сгенерированный
поставляемый
19
vendor/github.com/minio/minio-go/docs/API.md
сгенерированный
поставляемый
@@ -86,16 +86,27 @@ __Parameters__
|
||||
### NewWithRegion(endpoint, accessKeyID, secretAccessKey string, ssl bool, region string) (*Client, error)
|
||||
Initializes minio client, with region configured. Unlike New(), NewWithRegion avoids bucket-location lookup operations and it is slightly faster. Use this function when your application deals with a single region.
|
||||
|
||||
### NewWithOptions(endpoint string, options *Options) (*Client, error)
|
||||
Initializes minio client with options configured.
|
||||
|
||||
__Parameters__
|
||||
|
||||
|Param |Type |Description |
|
||||
|:---|:---| :---|
|
||||
|`endpoint` | _string_ |S3 compatible object storage endpoint |
|
||||
|`accessKeyID` |_string_ |Access key for the object storage |
|
||||
|`secretAccessKey` | _string_ |Secret key for the object storage |
|
||||
|`ssl` | _bool_ | If 'true' API requests will be secure (HTTPS), and insecure (HTTP) otherwise |
|
||||
|`region`| _string_ | Region for the object storage |
|
||||
|`opts` |_minio.Options_ | Options for constructing a new client|
|
||||
|
||||
__minio.Options__
|
||||
|
||||
|Field | Type | Description |
|
||||
|:--- |:--- | :--- |
|
||||
| `opts.Creds` | _*credentials.Credentials_ | Access Credentials|
|
||||
| `opts.Secure` | _bool_ | If 'true' API requests will be secure (HTTPS), and insecure (HTTP) otherwise |
|
||||
| `opts.Region` | _string_ | region |
|
||||
| `opts.BucketLookup` | _BucketLookupType_ | Bucket lookup type can be one of the following values |
|
||||
| | | _minio.BucketLookupDNS_ |
|
||||
| | | _minio.BucketLookupPath_ |
|
||||
| | | _minio.BucketLookupAuto_ |
|
||||
## 2. Bucket operations
|
||||
|
||||
<a name="MakeBucket"></a>
|
||||
|
||||
2
vendor/github.com/mitchellh/mapstructure/README.md
сгенерированный
поставляемый
2
vendor/github.com/mitchellh/mapstructure/README.md
сгенерированный
поставляемый
@@ -1,4 +1,4 @@
|
||||
# mapstructure [](https://godoc.org/github.com/mitchell/mapstructure)
|
||||
# mapstructure [](https://godoc.org/github.com/mitchellh/mapstructure)
|
||||
|
||||
mapstructure is a Go library for decoding generic map values to structures
|
||||
and vice versa, while providing helpful error handling.
|
||||
|
||||
19
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
сгенерированный
поставляемый
19
vendor/github.com/mitchellh/mapstructure/decode_hooks.go
сгенерированный
поставляемый
@@ -115,6 +115,25 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// StringToTimeHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to time.Time.
|
||||
func StringToTimeHookFunc(layout string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(time.Time{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
return time.Parse(layout, data.(string))
|
||||
}
|
||||
}
|
||||
|
||||
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
||||
// the decoder.
|
||||
//
|
||||
|
||||
30
vendor/github.com/mitchellh/mapstructure/decode_hooks_test.go
сгенерированный
поставляемый
30
vendor/github.com/mitchellh/mapstructure/decode_hooks_test.go
сгенерированный
поставляемый
@@ -153,6 +153,36 @@ func TestStringToTimeDurationHookFunc(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringToTimeHookFunc(t *testing.T) {
|
||||
strType := reflect.TypeOf("")
|
||||
timeType := reflect.TypeOf(time.Time{})
|
||||
cases := []struct {
|
||||
f, t reflect.Type
|
||||
layout string
|
||||
data interface{}
|
||||
result interface{}
|
||||
err bool
|
||||
}{
|
||||
{strType, timeType, time.RFC3339, "2006-01-02T15:04:05Z",
|
||||
time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
|
||||
{strType, timeType, time.RFC3339, "5", time.Time{}, true},
|
||||
{strType, strType, time.RFC3339, "5", "5", false},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
f := StringToTimeHookFunc(tc.layout)
|
||||
actual, err := DecodeHookExec(f, tc.f, tc.t, tc.data)
|
||||
if tc.err != (err != nil) {
|
||||
t.Fatalf("case %d: expected err %#v", i, tc.err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tc.result) {
|
||||
t.Fatalf(
|
||||
"case %d: expected %#v, got %#v",
|
||||
i, tc.result, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeaklyTypedHook(t *testing.T) {
|
||||
var f DecodeHookFunc = WeaklyTypedHook
|
||||
|
||||
|
||||
249
vendor/github.com/mitchellh/mapstructure/mapstructure.go
сгенерированный
поставляемый
249
vendor/github.com/mitchellh/mapstructure/mapstructure.go
сгенерированный
поставляемый
@@ -114,12 +114,12 @@ type Metadata struct {
|
||||
Unused []string
|
||||
}
|
||||
|
||||
// Decode takes a map and uses reflection to convert it into the
|
||||
// given Go native structure. val must be a pointer to a struct.
|
||||
func Decode(m interface{}, rawVal interface{}) error {
|
||||
// Decode takes an input structure and uses reflection to translate it to
|
||||
// the output structure. output must be a pointer to a map or struct.
|
||||
func Decode(input interface{}, output interface{}) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: rawVal,
|
||||
Result: output,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
@@ -127,7 +127,7 @@ func Decode(m interface{}, rawVal interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(m)
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// WeakDecode is the same as Decode but is shorthand to enable
|
||||
@@ -147,6 +147,40 @@ func WeakDecode(input, output interface{}) error {
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// DecodeMetadata is the same as Decode, but is shorthand to
|
||||
// enable metadata collection. See DecoderConfig for more info.
|
||||
func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: metadata,
|
||||
Result: output,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// WeakDecodeMetadata is the same as Decode, but is shorthand to
|
||||
// enable both WeaklyTypedInput and metadata collection. See
|
||||
// DecoderConfig for more info.
|
||||
func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
|
||||
config := &DecoderConfig{
|
||||
Metadata: metadata,
|
||||
Result: output,
|
||||
WeaklyTypedInput: true,
|
||||
}
|
||||
|
||||
decoder, err := NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(input)
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder for the given configuration. Once
|
||||
// a decoder has been returned, the same configuration must not be used
|
||||
// again.
|
||||
@@ -184,70 +218,70 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
|
||||
// Decode decodes the given raw interface to the target pointer specified
|
||||
// by the configuration.
|
||||
func (d *Decoder) Decode(raw interface{}) error {
|
||||
return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem())
|
||||
func (d *Decoder) Decode(input interface{}) error {
|
||||
return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||
}
|
||||
|
||||
// Decodes an unknown data type into a specific reflection value.
|
||||
func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error {
|
||||
if data == nil {
|
||||
// If the data is nil, then we don't set anything.
|
||||
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
|
||||
if input == nil {
|
||||
// If the input is nil, then we don't set anything.
|
||||
return nil
|
||||
}
|
||||
|
||||
dataVal := reflect.ValueOf(data)
|
||||
if !dataVal.IsValid() {
|
||||
// If the data value is invalid, then we just set the value
|
||||
inputVal := reflect.ValueOf(input)
|
||||
if !inputVal.IsValid() {
|
||||
// If the input value is invalid, then we just set the value
|
||||
// to be the zero value.
|
||||
val.Set(reflect.Zero(val.Type()))
|
||||
outVal.Set(reflect.Zero(outVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.config.DecodeHook != nil {
|
||||
// We have a DecodeHook, so let's pre-process the data.
|
||||
// We have a DecodeHook, so let's pre-process the input.
|
||||
var err error
|
||||
data, err = DecodeHookExec(
|
||||
input, err = DecodeHookExec(
|
||||
d.config.DecodeHook,
|
||||
dataVal.Type(), val.Type(), data)
|
||||
inputVal.Type(), outVal.Type(), input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
dataKind := getKind(val)
|
||||
switch dataKind {
|
||||
inputKind := getKind(outVal)
|
||||
switch inputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, data, val)
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, data, val)
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, data, val)
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, data, val)
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, data, val)
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, data, val)
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, data, val)
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, data, val)
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
err = d.decodePtr(name, data, val)
|
||||
err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, data, val)
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, data, val)
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, data, val)
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, dataKind)
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, inputKind)
|
||||
}
|
||||
|
||||
// If we reached here, then we successfully decoded SOMETHING, so
|
||||
// mark the key as used if we're tracking metadata.
|
||||
// mark the key as used if we're tracking metainput.
|
||||
if d.config.Metadata != nil && name != "" {
|
||||
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
|
||||
}
|
||||
@@ -258,6 +292,9 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
|
||||
// This decodes a basic type (bool, int, string, etc.) and sets the
|
||||
// value to "data" of that type.
|
||||
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
|
||||
if val.IsValid() && val.Elem().IsValid() {
|
||||
return d.decode(name, data, val.Elem())
|
||||
}
|
||||
dataVal := reflect.ValueOf(data)
|
||||
if !dataVal.IsValid() {
|
||||
dataVal = reflect.Zero(val.Type())
|
||||
@@ -499,34 +536,50 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
||||
valMap = reflect.MakeMap(mapType)
|
||||
}
|
||||
|
||||
// Check input type
|
||||
// Check input type and based on the input type jump to the proper func
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
if dataVal.Kind() != reflect.Map {
|
||||
// In weak mode, we accept a slice of maps as an input...
|
||||
switch dataVal.Kind() {
|
||||
case reflect.Map:
|
||||
return d.decodeMapFromMap(name, dataVal, val, valMap)
|
||||
|
||||
case reflect.Struct:
|
||||
return d.decodeMapFromStruct(name, dataVal, val, valMap)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
if d.config.WeaklyTypedInput {
|
||||
switch dataVal.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
// Special case for BC reasons (covered by tests)
|
||||
if dataVal.Len() == 0 {
|
||||
val.Set(valMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < dataVal.Len(); i++ {
|
||||
err := d.decode(
|
||||
fmt.Sprintf("%s[%d]", name, i),
|
||||
dataVal.Index(i).Interface(), val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return d.decodeMapFromSlice(name, dataVal, val, valMap)
|
||||
}
|
||||
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||
// Special case for BC reasons (covered by tests)
|
||||
if dataVal.Len() == 0 {
|
||||
val.Set(valMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < dataVal.Len(); i++ {
|
||||
err := d.decode(
|
||||
fmt.Sprintf("%s[%d]", name, i),
|
||||
dataVal.Index(i).Interface(), val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||
valType := val.Type()
|
||||
valKeyType := valType.Key()
|
||||
valElemType := valType.Elem()
|
||||
|
||||
// Accumulate errors
|
||||
errors := make([]string, 0)
|
||||
@@ -563,22 +616,88 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
|
||||
typ := dataVal.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
// Get the StructField first since this is a cheap operation. If the
|
||||
// field is unexported, then ignore it.
|
||||
f := typ.Field(i)
|
||||
if f.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Next get the actual value of this field and verify it is assignable
|
||||
// to the map value.
|
||||
v := dataVal.Field(i)
|
||||
if !v.Type().AssignableTo(valMap.Type().Elem()) {
|
||||
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
|
||||
}
|
||||
|
||||
// Determine the name of the key in the map
|
||||
keyName := f.Name
|
||||
tagValue := f.Tag.Get(d.config.TagName)
|
||||
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
||||
if tagValue != "" {
|
||||
if tagValue == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
keyName = tagValue
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
// this is an embedded struct, so handle it differently
|
||||
case reflect.Struct:
|
||||
x := reflect.New(v.Type())
|
||||
x.Elem().Set(v)
|
||||
|
||||
vType := valMap.Type()
|
||||
vKeyType := vType.Key()
|
||||
vElemType := vType.Elem()
|
||||
mType := reflect.MapOf(vKeyType, vElemType)
|
||||
vMap := reflect.MakeMap(mType)
|
||||
|
||||
err := d.decode(keyName, x.Interface(), vMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
|
||||
|
||||
default:
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
|
||||
}
|
||||
}
|
||||
|
||||
if val.CanAddr() {
|
||||
val.Set(valMap)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error {
|
||||
// Create an element of the concrete (non pointer) type and decode
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
|
||||
realVal := val
|
||||
if realVal.IsNil() || d.config.ZeroFields {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
if val.CanSet() {
|
||||
realVal := val
|
||||
if realVal.IsNil() || d.config.ZeroFields {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
|
||||
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val.Set(realVal)
|
||||
val.Set(realVal)
|
||||
} else {
|
||||
if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -614,7 +733,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||
val.Set(reflect.MakeSlice(sliceType, 0, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
|
||||
return d.decodeSlice(name, []byte(dataVal.String()), val)
|
||||
// All other types we try to convert to the slice type
|
||||
// and "lift" it into it. i.e. a string becomes a string slice.
|
||||
default:
|
||||
@@ -622,7 +742,6 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||
return d.decodeSlice(name, []interface{}{data}, val)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
"'%s': source data must be an array or slice, got %s", name, dataValKind)
|
||||
|
||||
|
||||
340
vendor/github.com/mitchellh/mapstructure/mapstructure_test.go
сгенерированный
поставляемый
340
vendor/github.com/mitchellh/mapstructure/mapstructure_test.go
сгенерированный
поставляемый
@@ -128,6 +128,7 @@ type TypeConversionResult struct {
|
||||
FloatToBool bool
|
||||
FloatToString string
|
||||
SliceUint8ToString string
|
||||
StringToSliceUint8 []byte
|
||||
ArrayUint8ToString string
|
||||
StringToInt int
|
||||
StringToUint uint
|
||||
@@ -248,6 +249,32 @@ func TestBasic_Merge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test for issue #46.
|
||||
func TestBasic_Struct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vdata": map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
},
|
||||
}
|
||||
|
||||
var result, inner Basic
|
||||
result.Vdata = &inner
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err)
|
||||
}
|
||||
expected := Basic{
|
||||
Vdata: &Basic{
|
||||
Vstring: "foo",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode_BasicSquash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -626,6 +653,7 @@ func TestDecode_TypeConversion(t *testing.T) {
|
||||
"FloatToBool": 42.42,
|
||||
"FloatToString": 42.42,
|
||||
"SliceUint8ToString": []uint8("foo"),
|
||||
"StringToSliceUint8": "foo",
|
||||
"ArrayUint8ToString": [3]uint8{'f', 'o', 'o'},
|
||||
"StringToInt": "42",
|
||||
"StringToUint": "42",
|
||||
@@ -671,6 +699,7 @@ func TestDecode_TypeConversion(t *testing.T) {
|
||||
FloatToBool: true,
|
||||
FloatToString: "42.42",
|
||||
SliceUint8ToString: "foo",
|
||||
StringToSliceUint8: []byte("foo"),
|
||||
ArrayUint8ToString: "foo",
|
||||
StringToInt: 42,
|
||||
StringToUint: 42,
|
||||
@@ -926,6 +955,56 @@ func TestNestedTypePointer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test for issue #46.
|
||||
func TestNestedTypeInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": &map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"vint": 42,
|
||||
"vbool": true,
|
||||
|
||||
"vdata": map[string]interface{}{
|
||||
"vstring": "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var result NestedPointer
|
||||
result.Vbar = new(Basic)
|
||||
result.Vbar.Vdata = new(Basic)
|
||||
err := Decode(input, &result)
|
||||
if err != nil {
|
||||
t.Fatalf("got an err: %s", err.Error())
|
||||
}
|
||||
|
||||
if result.Vfoo != "foo" {
|
||||
t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
|
||||
}
|
||||
|
||||
if result.Vbar.Vstring != "foo" {
|
||||
t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
|
||||
}
|
||||
|
||||
if result.Vbar.Vint != 42 {
|
||||
t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
|
||||
}
|
||||
|
||||
if result.Vbar.Vbool != true {
|
||||
t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
|
||||
}
|
||||
|
||||
if result.Vbar.Vextra != "" {
|
||||
t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
|
||||
}
|
||||
|
||||
if result.Vbar.Vdata.(*Basic).Vstring != "bar" {
|
||||
t.Errorf("vstring value should be 'bar': %#v", result.Vbar.Vdata.(*Basic).Vstring)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1112,6 +1191,197 @@ func TestArrayToMap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapOutputForStructuredInputs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in interface{}
|
||||
target interface{}
|
||||
out interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"basic struct input",
|
||||
&Basic{
|
||||
Vstring: "vstring",
|
||||
Vint: 2,
|
||||
Vuint: 3,
|
||||
Vbool: true,
|
||||
Vfloat: 4.56,
|
||||
Vextra: "vextra",
|
||||
vsilent: true,
|
||||
Vdata: []byte("data"),
|
||||
},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{
|
||||
"Vstring": "vstring",
|
||||
"Vint": 2,
|
||||
"Vuint": uint(3),
|
||||
"Vbool": true,
|
||||
"Vfloat": 4.56,
|
||||
"Vextra": "vextra",
|
||||
"Vdata": []byte("data"),
|
||||
"VjsonInt": 0,
|
||||
"VjsonFloat": 0.0,
|
||||
"VjsonNumber": json.Number(""),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"embedded struct input",
|
||||
&Embedded{
|
||||
Vunique: "vunique",
|
||||
Basic: Basic{
|
||||
Vstring: "vstring",
|
||||
Vint: 2,
|
||||
Vuint: 3,
|
||||
Vbool: true,
|
||||
Vfloat: 4.56,
|
||||
Vextra: "vextra",
|
||||
vsilent: true,
|
||||
Vdata: []byte("data"),
|
||||
},
|
||||
},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{
|
||||
"Vunique": "vunique",
|
||||
"Basic": map[string]interface{}{
|
||||
"Vstring": "vstring",
|
||||
"Vint": 2,
|
||||
"Vuint": uint(3),
|
||||
"Vbool": true,
|
||||
"Vfloat": 4.56,
|
||||
"Vextra": "vextra",
|
||||
"Vdata": []byte("data"),
|
||||
"VjsonInt": 0,
|
||||
"VjsonFloat": 0.0,
|
||||
"VjsonNumber": json.Number(""),
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"slice input - should error",
|
||||
[]string{"foo", "bar"},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"struct with slice property",
|
||||
&Slice{
|
||||
Vfoo: "vfoo",
|
||||
Vbar: []string{"foo", "bar"},
|
||||
},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{
|
||||
"Vfoo": "vfoo",
|
||||
"Vbar": []string{"foo", "bar"},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"struct with slice of struct property",
|
||||
&SliceOfStruct{
|
||||
Value: []Basic{
|
||||
Basic{
|
||||
Vstring: "vstring",
|
||||
Vint: 2,
|
||||
Vuint: 3,
|
||||
Vbool: true,
|
||||
Vfloat: 4.56,
|
||||
Vextra: "vextra",
|
||||
vsilent: true,
|
||||
Vdata: []byte("data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{
|
||||
"Value": []Basic{
|
||||
Basic{
|
||||
Vstring: "vstring",
|
||||
Vint: 2,
|
||||
Vuint: 3,
|
||||
Vbool: true,
|
||||
Vfloat: 4.56,
|
||||
Vextra: "vextra",
|
||||
vsilent: true,
|
||||
Vdata: []byte("data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"struct with map property",
|
||||
&Map{
|
||||
Vfoo: "vfoo",
|
||||
Vother: map[string]string{"vother": "vother"},
|
||||
},
|
||||
&map[string]interface{}{},
|
||||
&map[string]interface{}{
|
||||
"Vfoo": "vfoo",
|
||||
"Vother": map[string]string{
|
||||
"vother": "vother",
|
||||
}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"tagged struct",
|
||||
&Tagged{
|
||||
Extra: "extra",
|
||||
Value: "value",
|
||||
},
|
||||
&map[string]string{},
|
||||
&map[string]string{
|
||||
"bar": "extra",
|
||||
"foo": "value",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"omit tag struct",
|
||||
&struct {
|
||||
Value string `mapstructure:"value"`
|
||||
Omit string `mapstructure:"-"`
|
||||
}{
|
||||
Value: "value",
|
||||
Omit: "omit",
|
||||
},
|
||||
&map[string]string{},
|
||||
&map[string]string{
|
||||
"value": "value",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"decode to wrong map type",
|
||||
&struct {
|
||||
Value string
|
||||
}{
|
||||
Value: "string",
|
||||
},
|
||||
&map[string]int{},
|
||||
&map[string]int{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := Decode(tt.in, tt.target); (err != nil) != tt.wantErr {
|
||||
t.Fatalf("%q: TestMapOutputForStructuredInputs() unexpected error: %s", tt.name, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.out, tt.target) {
|
||||
t.Fatalf("%q: TestMapOutputForStructuredInputs() expected: %#v, got: %#v", tt.name, tt.out, tt.target)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1171,6 +1441,39 @@ func TestInvalidType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"vfoo": "foo",
|
||||
"vbar": map[string]interface{}{
|
||||
"vstring": "foo",
|
||||
"Vuint": 42,
|
||||
"foo": "bar",
|
||||
},
|
||||
"bar": "nil",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result Nested
|
||||
|
||||
err := DecodeMetadata(input, &result, &md)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err.Error())
|
||||
}
|
||||
|
||||
expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"}
|
||||
sort.Strings(md.Keys)
|
||||
if !reflect.DeepEqual(md.Keys, expectedKeys) {
|
||||
t.Fatalf("bad keys: %#v", md.Keys)
|
||||
}
|
||||
|
||||
expectedUnused := []string{"Vbar.foo", "bar"}
|
||||
if !reflect.DeepEqual(md.Unused, expectedUnused) {
|
||||
t.Fatalf("bad unused: %#v", md.Unused)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1311,6 +1614,43 @@ func TestWeakDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWeakDecodeMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
input := map[string]interface{}{
|
||||
"foo": "4",
|
||||
"bar": "value",
|
||||
"unused": "value",
|
||||
}
|
||||
|
||||
var md Metadata
|
||||
var result struct {
|
||||
Foo int
|
||||
Bar string
|
||||
}
|
||||
|
||||
if err := WeakDecodeMetadata(input, &result, &md); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if result.Foo != 4 {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
if result.Bar != "value" {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
|
||||
expectedKeys := []string{"Bar", "Foo"}
|
||||
sort.Strings(md.Keys)
|
||||
if !reflect.DeepEqual(md.Keys, expectedKeys) {
|
||||
t.Fatalf("bad keys: %#v", md.Keys)
|
||||
}
|
||||
|
||||
expectedUnused := []string{"unused"}
|
||||
if !reflect.DeepEqual(md.Unused, expectedUnused) {
|
||||
t.Fatalf("bad unused: %#v", md.Unused)
|
||||
}
|
||||
}
|
||||
|
||||
func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
|
||||
var result Slice
|
||||
err := Decode(input, &result)
|
||||
|
||||
2
vendor/github.com/olivere/elastic/.travis.yml
сгенерированный
поставляемый
2
vendor/github.com/olivere/elastic/.travis.yml
сгенерированный
поставляемый
@@ -12,4 +12,4 @@ services:
|
||||
- docker
|
||||
before_install:
|
||||
- sudo sysctl -w vm.max_map_count=262144
|
||||
- docker run -d --rm -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "bootstrap.memory_lock=true" -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" docker.elastic.co/elasticsearch/elasticsearch:6.1.2 elasticsearch -Expack.security.enabled=false -Enetwork.host=_local_,_site_ -Enetwork.publish_host=_local_
|
||||
- docker run -d --rm -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "bootstrap.memory_lock=true" -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" docker.elastic.co/elasticsearch/elasticsearch-oss:6.2.1 elasticsearch -Enetwork.host=_local_,_site_ -Enetwork.publish_host=_local_
|
||||
|
||||
11
vendor/github.com/olivere/elastic/CONTRIBUTORS
сгенерированный
поставляемый
11
vendor/github.com/olivere/elastic/CONTRIBUTORS
сгенерированный
поставляемый
@@ -68,9 +68,11 @@ Joe Buck [@four2five](https://github.com/four2five)
|
||||
John Barker [@j16r](https://github.com/j16r)
|
||||
John Goodall [@jgoodall](https://github.com/jgoodall)
|
||||
John Stanford [@jxstanford](https://github.com/jxstanford)
|
||||
Jonas Groenaas Drange [@semafor](https://github.com/semafor)
|
||||
Josh Chorlton [@jchorl](https://github.com/jchorl)
|
||||
jun [@coseyo](https://github.com/coseyo)
|
||||
Junpei Tsuji [@jun06t](https://github.com/jun06t)
|
||||
kartlee [@kartlee](https://github.com/kartlee)
|
||||
Keith Hatton [@khatton-ft](https://github.com/khatton-ft)
|
||||
kel [@liketic](https://github.com/liketic)
|
||||
Kenta SUZUKI [@suzuken](https://github.com/suzuken)
|
||||
@@ -98,10 +100,13 @@ Orne Brocaar [@brocaar](https://github.com/brocaar)
|
||||
Paul [@eyeamera](https://github.com/eyeamera)
|
||||
Pete C [@peteclark-ft](https://github.com/peteclark-ft)
|
||||
Radoslaw Wesolowski [r--w](https://github.com/r--w)
|
||||
Roman Colohanin [@zuzmic](https://github.com/zuzmic)
|
||||
Ryan Schmukler [@rschmukler](https://github.com/rschmukler)
|
||||
Ryan Wynn [@rwynn](https://github.com/rwynn)
|
||||
Sacheendra talluri [@sacheendra](https://github.com/sacheendra)
|
||||
Sean DuBois [@Sean-Der](https://github.com/Sean-Der)
|
||||
Shalin LK [@shalinlk](https://github.com/shalinlk)
|
||||
singham [@zhaochenxiao90](https://github.com/zhaochenxiao90)
|
||||
Stephen Kubovic [@stephenkubovic](https://github.com/stephenkubovic)
|
||||
Stuart Warren [@Woz](https://github.com/stuart-warren)
|
||||
Sulaiman [@salajlan](https://github.com/salajlan)
|
||||
@@ -111,13 +116,13 @@ Take [ww24](https://github.com/ww24)
|
||||
Tetsuya Morimoto [@t2y](https://github.com/t2y)
|
||||
TimeEmit [@TimeEmit](https://github.com/timeemit)
|
||||
TusharM [@tusharm](https://github.com/tusharm)
|
||||
zhangxin [@visaxin](https://github.com/visaxin)
|
||||
wangtuo [@wangtuo](https://github.com/wangtuo)
|
||||
Wédney Yuri [@wedneyyuri](https://github.com/wedneyyuri)
|
||||
wolfkdy [@wolfkdy](https://github.com/wolfkdy)
|
||||
Wyndham Blanton [@wyndhblb](https://github.com/wyndhblb)
|
||||
Yarden Bar [@ayashjorden](https://github.com/ayashjorden)
|
||||
zakthomas [@zakthomas](https://github.com/zakthomas)
|
||||
singham [@zhaochenxiao90](https://github.com/zhaochenxiao90)
|
||||
Yuya Kusakabe [@higebu](https://github.com/higebu)
|
||||
Zach [@snowzach](https://github.com/snowzach)
|
||||
zhangxin [@visaxin](https://github.com/visaxin)
|
||||
@林 [@zplzpl](https://github.com/zplzpl)
|
||||
Roman Colohanin [@zuzmic](https://github.com/zuzmic)
|
||||
|
||||
16
vendor/github.com/olivere/elastic/README.md
сгенерированный
поставляемый
16
vendor/github.com/olivere/elastic/README.md
сгенерированный
поставляемый
@@ -199,6 +199,7 @@ See the [wiki](https://github.com/olivere/elastic/wiki) for more details.
|
||||
- [x] Significant Terms
|
||||
- [x] Significant Text
|
||||
- [x] Terms
|
||||
- [x] Composite
|
||||
- Pipeline Aggregations
|
||||
- [x] Avg Bucket
|
||||
- [x] Derivative
|
||||
@@ -212,6 +213,7 @@ See the [wiki](https://github.com/olivere/elastic/wiki) for more details.
|
||||
- [x] Cumulative Sum
|
||||
- [x] Bucket Script
|
||||
- [x] Bucket Selector
|
||||
- [ ] Bucket Sort
|
||||
- [x] Serial Differencing
|
||||
- [x] Matrix Aggregations
|
||||
- [x] Matrix Stats
|
||||
@@ -234,17 +236,17 @@ See the [wiki](https://github.com/olivere/elastic/wiki) for more details.
|
||||
- [x] Update Indices Settings
|
||||
- [x] Get Settings
|
||||
- [x] Analyze
|
||||
- [x] Explain Analyze
|
||||
- [x] Index Templates
|
||||
- [ ] Shadow Replica Indices
|
||||
- [x] Indices Stats
|
||||
- [x] Indices Segments
|
||||
- [ ] Indices Recovery
|
||||
- [ ] Indices Shard Stores
|
||||
- [ ] Clear Cache
|
||||
- [x] Flush
|
||||
- [x] Synced Flush
|
||||
- [x] Refresh
|
||||
- [x] Force Merge
|
||||
- [ ] Upgrade
|
||||
|
||||
### cat APIs
|
||||
|
||||
@@ -267,6 +269,7 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
- [ ] cat shards
|
||||
- [ ] cat segments
|
||||
- [ ] cat snapshots
|
||||
- [ ] cat templates
|
||||
|
||||
### Cluster APIs
|
||||
|
||||
@@ -278,6 +281,8 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
- [ ] Cluster Update Settings
|
||||
- [x] Nodes Stats
|
||||
- [x] Nodes Info
|
||||
- [ ] Nodes Feature Usage
|
||||
- [ ] Remote Cluster Info
|
||||
- [x] Task Management API
|
||||
- [ ] Nodes hot_threads
|
||||
- [ ] Cluster Allocation Explain API
|
||||
@@ -297,6 +302,7 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
- Term level queries
|
||||
- [x] Term Query
|
||||
- [x] Terms Query
|
||||
- [x] Terms Set Query
|
||||
- [x] Range Query
|
||||
- [x] Exists Query
|
||||
- [x] Prefix Query
|
||||
@@ -311,7 +317,6 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
- [x] Dis Max Query
|
||||
- [x] Function Score Query
|
||||
- [x] Boosting Query
|
||||
- [x] Indices Query
|
||||
- Joining queries
|
||||
- [x] Nested Query
|
||||
- [x] Has Child Query
|
||||
@@ -321,12 +326,9 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
- [ ] GeoShape Query
|
||||
- [x] Geo Bounding Box Query
|
||||
- [x] Geo Distance Query
|
||||
- [ ] Geo Distance Range Query
|
||||
- [x] Geo Polygon Query
|
||||
- [ ] Geohash Cell Query
|
||||
- Specialized queries
|
||||
- [x] More Like This Query
|
||||
- [x] Template Query
|
||||
- [x] Script Query
|
||||
- [x] Percolate Query
|
||||
- Span queries
|
||||
@@ -346,7 +348,7 @@ The cat APIs are not implemented as of now. We think they are better suited for
|
||||
|
||||
- Snapshot and Restore
|
||||
- [x] Repositories
|
||||
- [ ] Snapshot
|
||||
- [x] Snapshot
|
||||
- [ ] Restore
|
||||
- [ ] Snapshot status
|
||||
- [ ] Monitoring snapshot/restore status
|
||||
|
||||
61
vendor/github.com/olivere/elastic/bulk_processor.go
сгенерированный
поставляемый
61
vendor/github.com/olivere/elastic/bulk_processor.go
сгенерированный
поставляемый
@@ -6,6 +6,7 @@ package elastic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -121,7 +122,7 @@ func (s *BulkProcessorService) Stats(wantStats bool) *BulkProcessorService {
|
||||
return s
|
||||
}
|
||||
|
||||
// Set the backoff strategy to use for errors
|
||||
// Backoff sets the backoff strategy to use for errors.
|
||||
func (s *BulkProcessorService) Backoff(backoff Backoff) *BulkProcessorService {
|
||||
s.backoff = backoff
|
||||
return s
|
||||
@@ -248,6 +249,8 @@ type BulkProcessor struct {
|
||||
|
||||
statsMu sync.Mutex // guards the following block
|
||||
stats *BulkProcessorStats
|
||||
|
||||
stopReconnC chan struct{} // channel to signal stop reconnection attempts
|
||||
}
|
||||
|
||||
func newBulkProcessor(
|
||||
@@ -293,6 +296,7 @@ func (p *BulkProcessor) Start(ctx context.Context) error {
|
||||
p.requestsC = make(chan BulkableRequest)
|
||||
p.executionId = 0
|
||||
p.stats = newBulkProcessorStats(p.numWorkers)
|
||||
p.stopReconnC = make(chan struct{})
|
||||
|
||||
// Create and start up workers.
|
||||
p.workers = make([]*bulkWorker, p.numWorkers)
|
||||
@@ -331,6 +335,12 @@ func (p *BulkProcessor) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tell connection checkers to stop
|
||||
if p.stopReconnC != nil {
|
||||
close(p.stopReconnC)
|
||||
p.stopReconnC = nil
|
||||
}
|
||||
|
||||
// Stop flusher (if enabled)
|
||||
if p.flusherStopC != nil {
|
||||
p.flusherStopC <- struct{}{}
|
||||
@@ -436,29 +446,43 @@ func (w *bulkWorker) work(ctx context.Context) {
|
||||
|
||||
var stop bool
|
||||
for !stop {
|
||||
var err error
|
||||
select {
|
||||
case req, open := <-w.p.requestsC:
|
||||
if open {
|
||||
// Received a new request
|
||||
w.service.Add(req)
|
||||
if w.commitRequired() {
|
||||
w.commit(ctx) // TODO swallow errors here?
|
||||
err = w.commit(ctx)
|
||||
}
|
||||
} else {
|
||||
// Channel closed: Stop.
|
||||
stop = true
|
||||
if w.service.NumberOfActions() > 0 {
|
||||
w.commit(ctx) // TODO swallow errors here?
|
||||
err = w.commit(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
case <-w.flushC:
|
||||
// Commit outstanding requests
|
||||
if w.service.NumberOfActions() > 0 {
|
||||
w.commit(ctx) // TODO swallow errors here?
|
||||
err = w.commit(ctx)
|
||||
}
|
||||
w.flushAckC <- struct{}{}
|
||||
}
|
||||
if !stop && err != nil {
|
||||
waitForActive := func() {
|
||||
// Add back pressure to prevent Add calls from filling up the request queue
|
||||
ready := make(chan struct{})
|
||||
go w.waitForActiveConnection(ready)
|
||||
<-ready
|
||||
}
|
||||
if _, ok := err.(net.Error); ok {
|
||||
waitForActive()
|
||||
} else if IsConnErr(err) {
|
||||
waitForActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,6 +535,35 @@ func (w *bulkWorker) commit(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *bulkWorker) waitForActiveConnection(ready chan<- struct{}) {
|
||||
defer close(ready)
|
||||
|
||||
t := time.NewTicker(5 * time.Second)
|
||||
defer t.Stop()
|
||||
|
||||
client := w.p.c
|
||||
stopReconnC := w.p.stopReconnC
|
||||
w.p.c.errorf("elastic: bulk processor %q is waiting for an active connection", w.p.name)
|
||||
|
||||
// loop until a health check finds at least 1 active connection or the reconnection channel is closed
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-stopReconnC:
|
||||
if !ok {
|
||||
w.p.c.errorf("elastic: bulk processor %q active connection check interrupted", w.p.name)
|
||||
return
|
||||
}
|
||||
case <-t.C:
|
||||
client.healthcheck(time.Duration(3)*time.Second, true)
|
||||
if client.mustActiveConn() == nil {
|
||||
// found an active connection
|
||||
// exit and signal done to the WaitGroup
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *bulkWorker) updateStats(res *BulkResponse) {
|
||||
// Update stats
|
||||
if res != nil {
|
||||
|
||||
8
vendor/github.com/olivere/elastic/client.go
сгенерированный
поставляемый
8
vendor/github.com/olivere/elastic/client.go
сгенерированный
поставляемый
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
const (
|
||||
// Version is the current version of Elastic.
|
||||
Version = "6.1.4"
|
||||
Version = "6.1.7"
|
||||
|
||||
// DefaultURL is the default endpoint of Elasticsearch on the local machine.
|
||||
// It is used e.g. when initializing a new Client without a specific URL.
|
||||
@@ -1778,9 +1778,3 @@ func (c *Client) WaitForGreenStatus(timeout string) error {
|
||||
func (c *Client) WaitForYellowStatus(timeout string) error {
|
||||
return c.WaitForStatus("yellow", timeout)
|
||||
}
|
||||
|
||||
// IsConnError unwraps the given error value and checks if it is equal to
|
||||
// elastic.ErrNoClient.
|
||||
func IsConnErr(err error) bool {
|
||||
return errors.Cause(err) == ErrNoClient
|
||||
}
|
||||
|
||||
8
vendor/github.com/olivere/elastic/errors.go
сгенерированный
поставляемый
8
vendor/github.com/olivere/elastic/errors.go
сгенерированный
поставляемый
@@ -9,6 +9,8 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// checkResponse will return an error if the request/response indicates
|
||||
@@ -89,6 +91,12 @@ func (e *Error) Error() string {
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnErr returns true if the error indicates that Elastic could not
|
||||
// find an Elasticsearch host to connect to.
|
||||
func IsConnErr(err error) bool {
|
||||
return err == ErrNoClient || errors.Cause(err) == ErrNoClient
|
||||
}
|
||||
|
||||
// IsNotFound returns true if the given error indicates that Elasticsearch
|
||||
// returned HTTP status 404. The err parameter can be of type *elastic.Error,
|
||||
// elastic.Error, *http.Response or int (indicating the HTTP status code).
|
||||
|
||||
39
vendor/github.com/olivere/elastic/msearch.go
сгенерированный
поставляемый
39
vendor/github.com/olivere/elastic/msearch.go
сгенерированный
поставляемый
@@ -14,19 +14,17 @@ import (
|
||||
|
||||
// MultiSearch executes one or more searches in one roundtrip.
|
||||
type MultiSearchService struct {
|
||||
client *Client
|
||||
requests []*SearchRequest
|
||||
indices []string
|
||||
pretty bool
|
||||
routing string
|
||||
preference string
|
||||
client *Client
|
||||
requests []*SearchRequest
|
||||
indices []string
|
||||
pretty bool
|
||||
maxConcurrentRequests *int
|
||||
preFilterShardSize *int
|
||||
}
|
||||
|
||||
func NewMultiSearchService(client *Client) *MultiSearchService {
|
||||
builder := &MultiSearchService{
|
||||
client: client,
|
||||
requests: make([]*SearchRequest, 0),
|
||||
indices: make([]string, 0),
|
||||
client: client,
|
||||
}
|
||||
return builder
|
||||
}
|
||||
@@ -46,6 +44,16 @@ func (s *MultiSearchService) Pretty(pretty bool) *MultiSearchService {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) MaxConcurrentSearches(max int) *MultiSearchService {
|
||||
s.maxConcurrentRequests = &max
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) PreFilterShardSize(size int) *MultiSearchService {
|
||||
s.preFilterShardSize = &size
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *MultiSearchService) Do(ctx context.Context) (*MultiSearchResult, error) {
|
||||
// Build url
|
||||
path := "/_msearch"
|
||||
@@ -55,6 +63,12 @@ func (s *MultiSearchService) Do(ctx context.Context) (*MultiSearchResult, error)
|
||||
if s.pretty {
|
||||
params.Set("pretty", fmt.Sprintf("%v", s.pretty))
|
||||
}
|
||||
if v := s.maxConcurrentRequests; v != nil {
|
||||
params.Set("max_concurrent_searches", fmt.Sprintf("%v", *v))
|
||||
}
|
||||
if v := s.preFilterShardSize; v != nil {
|
||||
params.Set("pre_filter_shard_size", fmt.Sprintf("%v", *v))
|
||||
}
|
||||
|
||||
// Set body
|
||||
var lines []string
|
||||
@@ -68,14 +82,14 @@ func (s *MultiSearchService) Do(ctx context.Context) (*MultiSearchResult, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := json.Marshal(sr.Body())
|
||||
body, err := sr.Body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines = append(lines, string(header))
|
||||
lines = append(lines, string(body))
|
||||
lines = append(lines, body)
|
||||
}
|
||||
body := strings.Join(lines, "\n") + "\n" // Don't forget trailing \n
|
||||
body := strings.Join(lines, "\n") + "\n" // add trailing \n
|
||||
|
||||
// Get response
|
||||
res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
|
||||
@@ -96,6 +110,7 @@ func (s *MultiSearchService) Do(ctx context.Context) (*MultiSearchResult, error)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MultiSearchResult is the outcome of running a multi-search operation.
|
||||
type MultiSearchResult struct {
|
||||
Responses []*SearchResult `json:"responses,omitempty"`
|
||||
}
|
||||
|
||||
105
vendor/github.com/olivere/elastic/msearch_test.go
сгенерированный
поставляемый
105
vendor/github.com/olivere/elastic/msearch_test.go
сгенерированный
поставляемый
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
func TestMultiSearch(t *testing.T) {
|
||||
client := setupTestClientAndCreateIndex(t)
|
||||
// client := setupTestClientAndCreateIndexAndLog(t)
|
||||
|
||||
tweet1 := tweet{
|
||||
User: "olivere",
|
||||
@@ -60,6 +61,110 @@ func TestMultiSearch(t *testing.T) {
|
||||
sreq2 := NewSearchRequest().Index(testIndexName).Type("doc").
|
||||
Source(NewSearchSource().Query(q2))
|
||||
|
||||
searchResult, err := client.MultiSearch().
|
||||
Add(sreq1, sreq2).
|
||||
Pretty(true).
|
||||
Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if searchResult.Responses == nil {
|
||||
t.Fatal("expected responses != nil; got nil")
|
||||
}
|
||||
if len(searchResult.Responses) != 2 {
|
||||
t.Fatalf("expected 2 responses; got %d", len(searchResult.Responses))
|
||||
}
|
||||
|
||||
sres := searchResult.Responses[0]
|
||||
if sres.Hits == nil {
|
||||
t.Errorf("expected Hits != nil; got nil")
|
||||
}
|
||||
if sres.Hits.TotalHits != 3 {
|
||||
t.Errorf("expected Hits.TotalHits = %d; got %d", 3, sres.Hits.TotalHits)
|
||||
}
|
||||
if len(sres.Hits.Hits) != 3 {
|
||||
t.Errorf("expected len(Hits.Hits) = %d; got %d", 3, len(sres.Hits.Hits))
|
||||
}
|
||||
for _, hit := range sres.Hits.Hits {
|
||||
if hit.Index != testIndexName {
|
||||
t.Errorf("expected Hits.Hit.Index = %q; got %q", testIndexName, hit.Index)
|
||||
}
|
||||
item := make(map[string]interface{})
|
||||
err := json.Unmarshal(*hit.Source, &item)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
sres = searchResult.Responses[1]
|
||||
if sres.Hits == nil {
|
||||
t.Errorf("expected Hits != nil; got nil")
|
||||
}
|
||||
if sres.Hits.TotalHits != 2 {
|
||||
t.Errorf("expected Hits.TotalHits = %d; got %d", 2, sres.Hits.TotalHits)
|
||||
}
|
||||
if len(sres.Hits.Hits) != 2 {
|
||||
t.Errorf("expected len(Hits.Hits) = %d; got %d", 2, len(sres.Hits.Hits))
|
||||
}
|
||||
for _, hit := range sres.Hits.Hits {
|
||||
if hit.Index != testIndexName {
|
||||
t.Errorf("expected Hits.Hit.Index = %q; got %q", testIndexName, hit.Index)
|
||||
}
|
||||
item := make(map[string]interface{})
|
||||
err := json.Unmarshal(*hit.Source, &item)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiSearchWithStrings(t *testing.T) {
|
||||
client := setupTestClientAndCreateIndex(t)
|
||||
// client := setupTestClientAndCreateIndexAndLog(t)
|
||||
|
||||
tweet1 := tweet{
|
||||
User: "olivere",
|
||||
Message: "Welcome to Golang and Elasticsearch.",
|
||||
Tags: []string{"golang", "elasticsearch"},
|
||||
}
|
||||
tweet2 := tweet{
|
||||
User: "olivere",
|
||||
Message: "Another unrelated topic.",
|
||||
Tags: []string{"golang"},
|
||||
}
|
||||
tweet3 := tweet{
|
||||
User: "sandrae",
|
||||
Message: "Cycling is fun.",
|
||||
Tags: []string{"sports", "cycling"},
|
||||
}
|
||||
|
||||
// Add all documents
|
||||
_, err := client.Index().Index(testIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Index().Index(testIndexName).Type("doc").Id("2").BodyJson(&tweet2).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Index().Index(testIndexName).Type("doc").Id("3").BodyJson(&tweet3).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Flush().Index(testIndexName).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Spawn two search queries with one roundtrip
|
||||
sreq1 := NewSearchRequest().Index(testIndexName, testIndexName2).
|
||||
Source(`{"query":{"match_all":{}}}`)
|
||||
sreq2 := NewSearchRequest().Index(testIndexName).Type("doc").
|
||||
Source(`{"query":{"term":{"tags":"golang"}}}`)
|
||||
|
||||
searchResult, err := client.MultiSearch().
|
||||
Add(sreq1, sreq2).
|
||||
Do(context.TODO())
|
||||
|
||||
149
vendor/github.com/olivere/elastic/recipes/bulk_processor/main.go
сгенерированный
поставляемый
Обычный файл
149
vendor/github.com/olivere/elastic/recipes/bulk_processor/main.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,149 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
// BulkProcessor runs a bulk processing job that fills an index
|
||||
// given certain criteria like flush interval etc.
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// bulk_processor -url=http://127.0.0.1:9200/bulk-processor-test?sniff=false -n=100000 -flush-interval=1s
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/olivere/elastic"
|
||||
"github.com/olivere/elastic/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
url = flag.String("url", "http://localhost:9200/bulk-processor-test", "Elasticsearch URL")
|
||||
numWorkers = flag.Int("num-workers", 4, "Number of workers")
|
||||
n = flag.Int64("n", -1, "Number of documents to process (-1 for unlimited)")
|
||||
flushInterval = flag.Duration("flush-interval", 1*time.Second, "Flush interval")
|
||||
bulkActions = flag.Int("bulk-actions", 0, "Number of bulk actions before committing")
|
||||
bulkSize = flag.Int("bulk-size", 0, "Size of bulk requests before committing")
|
||||
)
|
||||
flag.Parse()
|
||||
log.SetFlags(0)
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Parse configuration from URL
|
||||
cfg, err := config.Parse(*url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create an Elasticsearch client from the parsed config
|
||||
client, err := elastic.NewClientFromConfig(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Drop old index
|
||||
exists, err := client.IndexExists(cfg.Index).Do(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if exists {
|
||||
_, err = client.DeleteIndex(cfg.Index).Do(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create processor
|
||||
bulkp := elastic.NewBulkProcessorService(client).
|
||||
Name("bulk-test-processor").
|
||||
Stats(true).
|
||||
Backoff(elastic.StopBackoff{}).
|
||||
FlushInterval(*flushInterval).
|
||||
Workers(*numWorkers)
|
||||
if *bulkActions > 0 {
|
||||
bulkp = bulkp.BulkActions(*bulkActions)
|
||||
}
|
||||
if *bulkSize > 0 {
|
||||
bulkp = bulkp.BulkSize(*bulkSize)
|
||||
}
|
||||
p, err := bulkp.Do(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var created int64
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-c
|
||||
errc <- nil
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := p.Close(); err != nil {
|
||||
errc <- err
|
||||
}
|
||||
}()
|
||||
|
||||
type Doc struct {
|
||||
Timestamp time.Time `json:"@timestamp"`
|
||||
}
|
||||
|
||||
for {
|
||||
current := atomic.AddInt64(&created, 1)
|
||||
if *n > 0 && current >= *n {
|
||||
errc <- nil
|
||||
return
|
||||
}
|
||||
r := elastic.NewBulkIndexRequest().
|
||||
Index(cfg.Index).
|
||||
Type("doc").
|
||||
Id(uuid.New().String()).
|
||||
Doc(Doc{Timestamp: time.Now()})
|
||||
p.Add(r)
|
||||
|
||||
time.Sleep(time.Duration(rand.Intn(1000)) * time.Microsecond)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
t := time.NewTicker(1 * time.Second)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
stats := p.Stats()
|
||||
written := atomic.LoadInt64(&created)
|
||||
var queued int64
|
||||
for _, w := range stats.Workers {
|
||||
queued += w.Queued
|
||||
}
|
||||
fmt.Printf("Queued=%5d Written=%8d Succeeded=%8d Failed=%8d Comitted=%6d Flushed=%6d\n",
|
||||
queued,
|
||||
written,
|
||||
stats.Succeeded,
|
||||
stats.Failed,
|
||||
stats.Committed,
|
||||
stats.Flushed,
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := <-errc; err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
10
vendor/github.com/olivere/elastic/reindex.go
сгенерированный
поставляемый
10
vendor/github.com/olivere/elastic/reindex.go
сгенерированный
поставляемый
@@ -20,6 +20,7 @@ type ReindexService struct {
|
||||
waitForActiveShards string
|
||||
waitForCompletion *bool
|
||||
requestsPerSecond *int
|
||||
slices *int
|
||||
body interface{}
|
||||
source *ReindexSource
|
||||
destination *ReindexDestination
|
||||
@@ -51,6 +52,12 @@ func (s *ReindexService) RequestsPerSecond(requestsPerSecond int) *ReindexServic
|
||||
return s
|
||||
}
|
||||
|
||||
// Slices specifies the number of slices this task should be divided into. Defaults to 1.
|
||||
func (s *ReindexService) Slices(slices int) *ReindexService {
|
||||
s.slices = &slices
|
||||
return s
|
||||
}
|
||||
|
||||
// Refresh indicates whether Elasticsearch should refresh the effected indexes
|
||||
// immediately.
|
||||
func (s *ReindexService) Refresh(refresh string) *ReindexService {
|
||||
@@ -179,6 +186,9 @@ func (s *ReindexService) buildURL() (string, url.Values, error) {
|
||||
if s.requestsPerSecond != nil {
|
||||
params.Set("requests_per_second", fmt.Sprintf("%v", *s.requestsPerSecond))
|
||||
}
|
||||
if s.slices != nil {
|
||||
params.Set("slices", fmt.Sprintf("%v", *s.slices))
|
||||
}
|
||||
if s.waitForActiveShards != "" {
|
||||
params.Set("wait_for_active_shards", s.waitForActiveShards)
|
||||
}
|
||||
|
||||
4
vendor/github.com/olivere/elastic/run-es.sh
сгенерированный
поставляемый
4
vendor/github.com/olivere/elastic/run-es.sh
сгенерированный
поставляемый
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
VERSION=${VERSION:=6.1.2}
|
||||
docker run --rm -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "bootstrap.memory_lock=true" -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" docker.elastic.co/elasticsearch/elasticsearch:$VERSION elasticsearch -Expack.security.enabled=false -Enetwork.host=_local_,_site_ -Enetwork.publish_host=_local_
|
||||
VERSION=${VERSION:=6.2.1}
|
||||
docker run --rm -p 9200:9200 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "bootstrap.memory_lock=true" -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" docker.elastic.co/elasticsearch/elasticsearch-oss:$VERSION elasticsearch -Enetwork.host=_local_,_site_ -Enetwork.publish_host=_local_
|
||||
|
||||
3
vendor/github.com/olivere/elastic/search.go
сгенерированный
поставляемый
3
vendor/github.com/olivere/elastic/search.go
сгенерированный
поставляемый
@@ -111,8 +111,7 @@ func (s *SearchService) TimeoutInMillis(timeoutInMillis int) *SearchService {
|
||||
}
|
||||
|
||||
// SearchType sets the search operation type. Valid values are:
|
||||
// "query_then_fetch", "query_and_fetch", "dfs_query_then_fetch",
|
||||
// "dfs_query_and_fetch", "count", "scan".
|
||||
// "dfs_query_then_fetch" and "query_then_fetch".
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.0/search-request-search-type.html
|
||||
// for details.
|
||||
func (s *SearchService) SearchType(searchType string) *SearchService {
|
||||
|
||||
70
vendor/github.com/olivere/elastic/search_aggs.go
сгенерированный
поставляемый
70
vendor/github.com/olivere/elastic/search_aggs.go
сгенерированный
поставляемый
@@ -653,6 +653,23 @@ func (a Aggregations) SerialDiff(name string) (*AggregationPipelineSimpleValue,
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Composite returns composite bucket aggregation results.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html
|
||||
// for details.
|
||||
func (a Aggregations) Composite(name string) (*AggregationBucketCompositeItems, bool) {
|
||||
if raw, found := a[name]; found {
|
||||
agg := new(AggregationBucketCompositeItems)
|
||||
if raw == nil {
|
||||
return agg, true
|
||||
}
|
||||
if err := json.Unmarshal(*raw, agg); err == nil {
|
||||
return agg, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// -- Single value metric --
|
||||
|
||||
// AggregationValueMetric is a single-value metric, returned e.g. by a
|
||||
@@ -1448,3 +1465,56 @@ func (a *AggregationPipelinePercentilesMetric) UnmarshalJSON(data []byte) error
|
||||
a.Aggregations = aggs
|
||||
return nil
|
||||
}
|
||||
|
||||
// -- Composite key items --
|
||||
|
||||
// AggregationBucketCompositeItems implements the response structure
|
||||
// for a bucket aggregation of type composite.
|
||||
type AggregationBucketCompositeItems struct {
|
||||
Aggregations
|
||||
|
||||
Buckets []*AggregationBucketCompositeItem //`json:"buckets"`
|
||||
Meta map[string]interface{} // `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON data and initializes an AggregationBucketCompositeItems structure.
|
||||
func (a *AggregationBucketCompositeItems) UnmarshalJSON(data []byte) error {
|
||||
var aggs map[string]*json.RawMessage
|
||||
if err := json.Unmarshal(data, &aggs); err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := aggs["buckets"]; ok && v != nil {
|
||||
json.Unmarshal(*v, &a.Buckets)
|
||||
}
|
||||
if v, ok := aggs["meta"]; ok && v != nil {
|
||||
json.Unmarshal(*v, &a.Meta)
|
||||
}
|
||||
a.Aggregations = aggs
|
||||
return nil
|
||||
}
|
||||
|
||||
// AggregationBucketCompositeItem is a single bucket of an AggregationBucketCompositeItems structure.
|
||||
type AggregationBucketCompositeItem struct {
|
||||
Aggregations
|
||||
|
||||
Key map[string]interface{} //`json:"key"`
|
||||
DocCount int64 //`json:"doc_count"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON data and initializes an AggregationBucketCompositeItem structure.
|
||||
func (a *AggregationBucketCompositeItem) UnmarshalJSON(data []byte) error {
|
||||
var aggs map[string]*json.RawMessage
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.UseNumber()
|
||||
if err := dec.Decode(&aggs); err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := aggs["key"]; ok && v != nil {
|
||||
json.Unmarshal(*v, &a.Key)
|
||||
}
|
||||
if v, ok := aggs["doc_count"]; ok && v != nil {
|
||||
json.Unmarshal(*v, &a.DocCount)
|
||||
}
|
||||
a.Aggregations = aggs
|
||||
return nil
|
||||
}
|
||||
|
||||
498
vendor/github.com/olivere/elastic/search_aggs_bucket_composite.go
сгенерированный
поставляемый
Обычный файл
498
vendor/github.com/olivere/elastic/search_aggs_bucket_composite.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,498 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// CompositeAggregation is a multi-bucket values source based aggregation
|
||||
// that can be used to calculate unique composite values from source documents.
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html
|
||||
// for details.
|
||||
type CompositeAggregation struct {
|
||||
after map[string]interface{}
|
||||
size *int
|
||||
sources []CompositeAggregationValuesSource
|
||||
subAggregations map[string]Aggregation
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
// NewCompositeAggregation creates a new CompositeAggregation.
|
||||
func NewCompositeAggregation() *CompositeAggregation {
|
||||
return &CompositeAggregation{
|
||||
sources: make([]CompositeAggregationValuesSource, 0),
|
||||
subAggregations: make(map[string]Aggregation),
|
||||
}
|
||||
}
|
||||
|
||||
// Size represents the number of composite buckets to return.
|
||||
// Defaults to 10 as of Elasticsearch 6.1.
|
||||
func (a *CompositeAggregation) Size(size int) *CompositeAggregation {
|
||||
a.size = &size
|
||||
return a
|
||||
}
|
||||
|
||||
// AggregateAfter sets the values that indicate which composite bucket this
|
||||
// request should "aggregate after".
|
||||
func (a *CompositeAggregation) AggregateAfter(after map[string]interface{}) *CompositeAggregation {
|
||||
a.after = after
|
||||
return a
|
||||
}
|
||||
|
||||
// Sources specifies the list of CompositeAggregationValuesSource instances to
|
||||
// use in the aggregation.
|
||||
func (a *CompositeAggregation) Sources(sources ...CompositeAggregationValuesSource) *CompositeAggregation {
|
||||
a.sources = append(a.sources, sources...)
|
||||
return a
|
||||
}
|
||||
|
||||
// SubAggregations of this aggregation.
|
||||
func (a *CompositeAggregation) SubAggregation(name string, subAggregation Aggregation) *CompositeAggregation {
|
||||
a.subAggregations[name] = subAggregation
|
||||
return a
|
||||
}
|
||||
|
||||
// Meta sets the meta data to be included in the aggregation response.
|
||||
func (a *CompositeAggregation) Meta(metaData map[string]interface{}) *CompositeAggregation {
|
||||
a.meta = metaData
|
||||
return a
|
||||
}
|
||||
|
||||
// Source returns the serializable JSON for this aggregation.
|
||||
func (a *CompositeAggregation) Source() (interface{}, error) {
|
||||
// Example:
|
||||
// {
|
||||
// "aggs" : {
|
||||
// "my_composite_agg" : {
|
||||
// "composite" : {
|
||||
// "sources": [
|
||||
// {"my_term": { "terms": { "field": "product" }}},
|
||||
// {"my_histo": { "histogram": { "field": "price", "interval": 5 }}},
|
||||
// {"my_date": { "date_histogram": { "field": "timestamp", "interval": "1d" }}},
|
||||
// ],
|
||||
// "size" : 10,
|
||||
// "after" : ["a", 2, "c"]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This method returns only the { "histogram" : { ... } } part.
|
||||
|
||||
source := make(map[string]interface{})
|
||||
opts := make(map[string]interface{})
|
||||
source["composite"] = opts
|
||||
|
||||
sources := make([]interface{}, len(a.sources))
|
||||
for i, s := range a.sources {
|
||||
src, err := s.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sources[i] = src
|
||||
}
|
||||
opts["sources"] = sources
|
||||
|
||||
if a.size != nil {
|
||||
opts["size"] = *a.size
|
||||
}
|
||||
|
||||
if a.after != nil {
|
||||
opts["after"] = a.after
|
||||
}
|
||||
|
||||
// AggregationBuilder (SubAggregations)
|
||||
if len(a.subAggregations) > 0 {
|
||||
aggsMap := make(map[string]interface{})
|
||||
source["aggregations"] = aggsMap
|
||||
for name, aggregate := range a.subAggregations {
|
||||
src, err := aggregate.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggsMap[name] = src
|
||||
}
|
||||
}
|
||||
|
||||
// Add Meta data if available
|
||||
if len(a.meta) > 0 {
|
||||
source["meta"] = a.meta
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// -- Generic interface for CompositeAggregationValues --
|
||||
|
||||
// CompositeAggregationValuesSource specifies the interface that
|
||||
// all implementations for CompositeAggregation's Sources method
|
||||
// need to implement.
|
||||
//
|
||||
// The different implementations are described in
|
||||
// https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html#_values_source_2.
|
||||
type CompositeAggregationValuesSource interface {
|
||||
Source() (interface{}, error)
|
||||
}
|
||||
|
||||
// -- CompositeAggregationTermsValuesSource --
|
||||
|
||||
// CompositeAggregationTermsValuesSource is a source for the CompositeAggregation that handles terms
|
||||
// it works very similar to a terms aggregation with slightly different syntax
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html#_terms
|
||||
// for details.
|
||||
type CompositeAggregationTermsValuesSource struct {
|
||||
name string
|
||||
field string
|
||||
script *Script
|
||||
valueType string
|
||||
missing interface{}
|
||||
order string
|
||||
}
|
||||
|
||||
// NewCompositeAggregationTermsValuesSource creates and initializes
|
||||
// a new CompositeAggregationTermsValuesSource.
|
||||
func NewCompositeAggregationTermsValuesSource(name string) *CompositeAggregationTermsValuesSource {
|
||||
return &CompositeAggregationTermsValuesSource{
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// Field to use for this source.
|
||||
func (a *CompositeAggregationTermsValuesSource) Field(field string) *CompositeAggregationTermsValuesSource {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
// Script to use for this source.
|
||||
func (a *CompositeAggregationTermsValuesSource) Script(script *Script) *CompositeAggregationTermsValuesSource {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// ValueType specifies the type of values produced by this source,
|
||||
// e.g. "string" or "date".
|
||||
func (a *CompositeAggregationTermsValuesSource) ValueType(valueType string) *CompositeAggregationTermsValuesSource {
|
||||
a.valueType = valueType
|
||||
return a
|
||||
}
|
||||
|
||||
// Order specifies the order in the values produced by this source.
|
||||
// It can be either "asc" or "desc".
|
||||
func (a *CompositeAggregationTermsValuesSource) Order(order string) *CompositeAggregationTermsValuesSource {
|
||||
a.order = order
|
||||
return a
|
||||
}
|
||||
|
||||
// Asc ensures the order of the values produced is ascending.
|
||||
func (a *CompositeAggregationTermsValuesSource) Asc() *CompositeAggregationTermsValuesSource {
|
||||
a.order = "asc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Desc ensures the order of the values produced is descending.
|
||||
func (a *CompositeAggregationTermsValuesSource) Desc() *CompositeAggregationTermsValuesSource {
|
||||
a.order = "desc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing specifies the value to use when the source finds a missing
|
||||
// value in a document.
|
||||
func (a *CompositeAggregationTermsValuesSource) Missing(missing interface{}) *CompositeAggregationTermsValuesSource {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
// Source returns the serializable JSON for this values source.
|
||||
func (a *CompositeAggregationTermsValuesSource) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
name := make(map[string]interface{})
|
||||
source[a.name] = name
|
||||
values := make(map[string]interface{})
|
||||
name["terms"] = values
|
||||
|
||||
// field
|
||||
if a.field != "" {
|
||||
values["field"] = a.field
|
||||
}
|
||||
|
||||
// script
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values["script"] = src
|
||||
}
|
||||
|
||||
// missing
|
||||
if a.missing != nil {
|
||||
values["missing"] = a.missing
|
||||
}
|
||||
|
||||
// value_type
|
||||
if a.valueType != "" {
|
||||
values["value_type"] = a.valueType
|
||||
}
|
||||
|
||||
// order
|
||||
if a.order != "" {
|
||||
values["order"] = a.order
|
||||
}
|
||||
|
||||
return source, nil
|
||||
|
||||
}
|
||||
|
||||
// -- CompositeAggregationHistogramValuesSource --
|
||||
|
||||
// CompositeAggregationHistogramValuesSource is a source for the CompositeAggregation that handles histograms
|
||||
// it works very similar to a terms histogram with slightly different syntax
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html#_histogram
|
||||
// for details.
|
||||
type CompositeAggregationHistogramValuesSource struct {
|
||||
name string
|
||||
field string
|
||||
script *Script
|
||||
valueType string
|
||||
missing interface{}
|
||||
order string
|
||||
interval float64
|
||||
}
|
||||
|
||||
// NewCompositeAggregationHistogramValuesSource creates and initializes
|
||||
// a new CompositeAggregationHistogramValuesSource.
|
||||
func NewCompositeAggregationHistogramValuesSource(name string, interval float64) *CompositeAggregationHistogramValuesSource {
|
||||
return &CompositeAggregationHistogramValuesSource{
|
||||
name: name,
|
||||
interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
// Field to use for this source.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Field(field string) *CompositeAggregationHistogramValuesSource {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
// Script to use for this source.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Script(script *Script) *CompositeAggregationHistogramValuesSource {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// ValueType specifies the type of values produced by this source,
|
||||
// e.g. "string" or "date".
|
||||
func (a *CompositeAggregationHistogramValuesSource) ValueType(valueType string) *CompositeAggregationHistogramValuesSource {
|
||||
a.valueType = valueType
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing specifies the value to use when the source finds a missing
|
||||
// value in a document.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Missing(missing interface{}) *CompositeAggregationHistogramValuesSource {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
// Order specifies the order in the values produced by this source.
|
||||
// It can be either "asc" or "desc".
|
||||
func (a *CompositeAggregationHistogramValuesSource) Order(order string) *CompositeAggregationHistogramValuesSource {
|
||||
a.order = order
|
||||
return a
|
||||
}
|
||||
|
||||
// Asc ensures the order of the values produced is ascending.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Asc() *CompositeAggregationHistogramValuesSource {
|
||||
a.order = "asc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Desc ensures the order of the values produced is descending.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Desc() *CompositeAggregationHistogramValuesSource {
|
||||
a.order = "desc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Interval specifies the interval to use.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Interval(interval float64) *CompositeAggregationHistogramValuesSource {
|
||||
a.interval = interval
|
||||
return a
|
||||
}
|
||||
|
||||
// Source returns the serializable JSON for this values source.
|
||||
func (a *CompositeAggregationHistogramValuesSource) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
name := make(map[string]interface{})
|
||||
source[a.name] = name
|
||||
values := make(map[string]interface{})
|
||||
name["histogram"] = values
|
||||
|
||||
// field
|
||||
if a.field != "" {
|
||||
values["field"] = a.field
|
||||
}
|
||||
|
||||
// script
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values["script"] = src
|
||||
}
|
||||
|
||||
// missing
|
||||
if a.missing != nil {
|
||||
values["missing"] = a.missing
|
||||
}
|
||||
|
||||
// value_type
|
||||
if a.valueType != "" {
|
||||
values["value_type"] = a.valueType
|
||||
}
|
||||
|
||||
// order
|
||||
if a.order != "" {
|
||||
values["order"] = a.order
|
||||
}
|
||||
|
||||
// Histogram-related properties
|
||||
values["interval"] = a.interval
|
||||
|
||||
return source, nil
|
||||
|
||||
}
|
||||
|
||||
// -- CompositeAggregationDateHistogramValuesSource --
|
||||
|
||||
// CompositeAggregationDateHistogramValuesSource is a source for the CompositeAggregation that handles date histograms
|
||||
// it works very similar to a date histogram aggregation with slightly different syntax
|
||||
//
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.1/search-aggregations-bucket-composite-aggregation.html#_date_histogram
|
||||
// for details.
|
||||
type CompositeAggregationDateHistogramValuesSource struct {
|
||||
name string
|
||||
field string
|
||||
script *Script
|
||||
valueType string
|
||||
missing interface{}
|
||||
order string
|
||||
interval interface{}
|
||||
timeZone string
|
||||
}
|
||||
|
||||
// NewCompositeAggregationDateHistogramValuesSource creates and initializes
|
||||
// a new CompositeAggregationDateHistogramValuesSource.
|
||||
func NewCompositeAggregationDateHistogramValuesSource(name string, interval interface{}) *CompositeAggregationDateHistogramValuesSource {
|
||||
return &CompositeAggregationDateHistogramValuesSource{
|
||||
name: name,
|
||||
interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
// Field to use for this source.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Field(field string) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.field = field
|
||||
return a
|
||||
}
|
||||
|
||||
// Script to use for this source.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Script(script *Script) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.script = script
|
||||
return a
|
||||
}
|
||||
|
||||
// ValueType specifies the type of values produced by this source,
|
||||
// e.g. "string" or "date".
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) ValueType(valueType string) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.valueType = valueType
|
||||
return a
|
||||
}
|
||||
|
||||
// Missing specifies the value to use when the source finds a missing
|
||||
// value in a document.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Missing(missing interface{}) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.missing = missing
|
||||
return a
|
||||
}
|
||||
|
||||
// Order specifies the order in the values produced by this source.
|
||||
// It can be either "asc" or "desc".
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Order(order string) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.order = order
|
||||
return a
|
||||
}
|
||||
|
||||
// Asc ensures the order of the values produced is ascending.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Asc() *CompositeAggregationDateHistogramValuesSource {
|
||||
a.order = "asc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Desc ensures the order of the values produced is descending.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Desc() *CompositeAggregationDateHistogramValuesSource {
|
||||
a.order = "desc"
|
||||
return a
|
||||
}
|
||||
|
||||
// Interval to use for the date histogram, e.g. "1d" or a numeric value like "60".
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Interval(interval interface{}) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.interval = interval
|
||||
return a
|
||||
}
|
||||
|
||||
// TimeZone to use for the dates.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) TimeZone(timeZone string) *CompositeAggregationDateHistogramValuesSource {
|
||||
a.timeZone = timeZone
|
||||
return a
|
||||
}
|
||||
|
||||
// Source returns the serializable JSON for this values source.
|
||||
func (a *CompositeAggregationDateHistogramValuesSource) Source() (interface{}, error) {
|
||||
source := make(map[string]interface{})
|
||||
name := make(map[string]interface{})
|
||||
source[a.name] = name
|
||||
values := make(map[string]interface{})
|
||||
name["date_histogram"] = values
|
||||
|
||||
// field
|
||||
if a.field != "" {
|
||||
values["field"] = a.field
|
||||
}
|
||||
|
||||
// script
|
||||
if a.script != nil {
|
||||
src, err := a.script.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values["script"] = src
|
||||
}
|
||||
|
||||
// missing
|
||||
if a.missing != nil {
|
||||
values["missing"] = a.missing
|
||||
}
|
||||
|
||||
// value_type
|
||||
if a.valueType != "" {
|
||||
values["value_type"] = a.valueType
|
||||
}
|
||||
|
||||
// order
|
||||
if a.order != "" {
|
||||
values["order"] = a.order
|
||||
}
|
||||
|
||||
// DateHistogram-related properties
|
||||
values["interval"] = a.interval
|
||||
|
||||
// timeZone
|
||||
if a.timeZone != "" {
|
||||
values["time_zone"] = a.timeZone
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
92
vendor/github.com/olivere/elastic/search_aggs_bucket_composite_test.go
сгенерированный
поставляемый
Обычный файл
92
vendor/github.com/olivere/elastic/search_aggs_bucket_composite_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,92 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompositeAggregation(t *testing.T) {
|
||||
agg := NewCompositeAggregation().
|
||||
Sources(
|
||||
NewCompositeAggregationTermsValuesSource("my_terms").Field("a_term").Missing("N/A").Order("asc"),
|
||||
NewCompositeAggregationHistogramValuesSource("my_histogram", 5).Field("price").Asc(),
|
||||
NewCompositeAggregationDateHistogramValuesSource("my_date_histogram", "1d").Field("purchase_date").Desc(),
|
||||
).
|
||||
Size(10).
|
||||
AggregateAfter(map[string]interface{}{
|
||||
"my_terms": "1",
|
||||
"my_histogram": 2,
|
||||
"my_date_histogram": "3",
|
||||
})
|
||||
src, err := agg.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"composite":{"after":{"my_date_histogram":"3","my_histogram":2,"my_terms":"1"},"size":10,"sources":[{"my_terms":{"terms":{"field":"a_term","missing":"N/A","order":"asc"}}},{"my_histogram":{"histogram":{"field":"price","interval":5,"order":"asc"}}},{"my_date_histogram":{"date_histogram":{"field":"purchase_date","interval":"1d","order":"desc"}}}]}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompositeAggregationTermsValuesSource(t *testing.T) {
|
||||
in := NewCompositeAggregationTermsValuesSource("products").
|
||||
Script(NewScript("doc['product'].value").Lang("painless"))
|
||||
src, err := in.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"products":{"terms":{"script":{"lang":"painless","source":"doc['product'].value"}}}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompositeAggregationHistogramValuesSource(t *testing.T) {
|
||||
in := NewCompositeAggregationHistogramValuesSource("histo", 5).
|
||||
Field("price")
|
||||
src, err := in.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"histo":{"histogram":{"field":"price","interval":5}}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompositeAggregationDateHistogramValuesSource(t *testing.T) {
|
||||
in := NewCompositeAggregationDateHistogramValuesSource("date", "1d").
|
||||
Field("timestamp")
|
||||
src, err := in.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"date":{"date_histogram":{"field":"timestamp","interval":"1d"}}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/olivere/elastic/search_aggs_bucket_date_range.go
сгенерированный
поставляемый
9
vendor/github.com/olivere/elastic/search_aggs_bucket_date_range.go
сгенерированный
поставляемый
@@ -23,6 +23,7 @@ type DateRangeAggregation struct {
|
||||
meta map[string]interface{}
|
||||
keyed *bool
|
||||
unmapped *bool
|
||||
timeZone string
|
||||
format string
|
||||
entries []DateRangeAggregationEntry
|
||||
}
|
||||
@@ -71,6 +72,11 @@ func (a *DateRangeAggregation) Unmapped(unmapped bool) *DateRangeAggregation {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) TimeZone(timeZone string) *DateRangeAggregation {
|
||||
a.timeZone = timeZone
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *DateRangeAggregation) Format(format string) *DateRangeAggregation {
|
||||
a.format = format
|
||||
return a
|
||||
@@ -178,6 +184,9 @@ func (a *DateRangeAggregation) Source() (interface{}, error) {
|
||||
if a.unmapped != nil {
|
||||
opts["unmapped"] = *a.unmapped
|
||||
}
|
||||
if a.timeZone != "" {
|
||||
opts["time_zone"] = a.timeZone
|
||||
}
|
||||
if a.format != "" {
|
||||
opts["format"] = a.format
|
||||
}
|
||||
|
||||
4
vendor/github.com/olivere/elastic/search_aggs_bucket_date_range_test.go
сгенерированный
поставляемый
4
vendor/github.com/olivere/elastic/search_aggs_bucket_date_range_test.go
сгенерированный
поставляемый
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDateRangeAggregation(t *testing.T) {
|
||||
agg := NewDateRangeAggregation().Field("created_at")
|
||||
agg := NewDateRangeAggregation().Field("created_at").TimeZone("UTC")
|
||||
agg = agg.AddRange(nil, "2012-12-31")
|
||||
agg = agg.AddRange("2013-01-01", "2013-12-31")
|
||||
agg = agg.AddRange("2014-01-01", nil)
|
||||
@@ -23,7 +23,7 @@ func TestDateRangeAggregation(t *testing.T) {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"date_range":{"field":"created_at","ranges":[{"to":"2012-12-31"},{"from":"2013-01-01","to":"2013-12-31"},{"from":"2014-01-01"}]}}`
|
||||
expected := `{"date_range":{"field":"created_at","ranges":[{"to":"2012-12-31"},{"from":"2013-01-01","to":"2013-12-31"},{"from":"2014-01-01"}],"time_zone":"UTC"}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
|
||||
441
vendor/github.com/olivere/elastic/search_aggs_test.go
сгенерированный
поставляемый
441
vendor/github.com/olivere/elastic/search_aggs_test.go
сгенерированный
поставляемый
@@ -13,13 +13,15 @@ import (
|
||||
)
|
||||
|
||||
func TestAggs(t *testing.T) {
|
||||
// client := setupTestClientAndCreateIndex(t, SetTraceLog(log.New(os.Stdout, "", log.LstdFlags)))
|
||||
//client := setupTestClientAndCreateIndex(t, SetTraceLog(log.New(os.Stdout, "", log.LstdFlags)))
|
||||
client := setupTestClientAndCreateIndex(t)
|
||||
|
||||
esversion, err := client.ElasticsearchVersion(DefaultURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
/*
|
||||
esversion, err := client.ElasticsearchVersion(DefaultURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
*/
|
||||
|
||||
tweet1 := tweet{
|
||||
User: "olivere",
|
||||
@@ -48,7 +50,7 @@ func TestAggs(t *testing.T) {
|
||||
}
|
||||
|
||||
// Add all documents
|
||||
_, err = client.Index().Index(testIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO())
|
||||
_, err := client.Index().Index(testIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -102,6 +104,11 @@ func TestAggs(t *testing.T) {
|
||||
topTagsAgg := NewTermsAggregation().Field("tags").Size(3).SubAggregation("top_tag_hits", topTagsHitsAgg)
|
||||
geoBoundsAgg := NewGeoBoundsAggregation().Field("location")
|
||||
geoHashAgg := NewGeoHashGridAggregation().Field("location").Precision(5)
|
||||
composite := NewCompositeAggregation().Sources(
|
||||
NewCompositeAggregationTermsValuesSource("composite_users").Field("user"),
|
||||
NewCompositeAggregationHistogramValuesSource("composite_retweets", 1).Field("retweets"),
|
||||
NewCompositeAggregationDateHistogramValuesSource("composite_created", "1m").Field("created"),
|
||||
)
|
||||
|
||||
// Run query
|
||||
builder := client.Search().Index(testIndexName).Query(all).Pretty(true)
|
||||
@@ -109,9 +116,7 @@ func TestAggs(t *testing.T) {
|
||||
builder = builder.Aggregation("users", usersAgg)
|
||||
builder = builder.Aggregation("retweets", retweetsAgg)
|
||||
builder = builder.Aggregation("avgRetweets", avgRetweetsAgg)
|
||||
if esversion >= "2.0" {
|
||||
builder = builder.Aggregation("avgRetweetsWithMeta", avgRetweetsWithMetaAgg)
|
||||
}
|
||||
builder = builder.Aggregation("avgRetweetsWithMeta", avgRetweetsWithMetaAgg)
|
||||
builder = builder.Aggregation("minRetweets", minRetweetsAgg)
|
||||
builder = builder.Aggregation("maxRetweets", maxRetweetsAgg)
|
||||
builder = builder.Aggregation("sumRetweets", sumRetweetsAgg)
|
||||
@@ -134,44 +139,41 @@ func TestAggs(t *testing.T) {
|
||||
builder = builder.Aggregation("top-tags", topTagsAgg)
|
||||
builder = builder.Aggregation("viewport", geoBoundsAgg)
|
||||
builder = builder.Aggregation("geohashed", geoHashAgg)
|
||||
if esversion >= "1.4" {
|
||||
// Unnamed filters
|
||||
countByUserAgg := NewFiltersAggregation().
|
||||
Filters(NewTermQuery("user", "olivere"), NewTermQuery("user", "sandrae"))
|
||||
builder = builder.Aggregation("countByUser", countByUserAgg)
|
||||
// Named filters
|
||||
countByUserAgg2 := NewFiltersAggregation().
|
||||
FilterWithName("olivere", NewTermQuery("user", "olivere")).
|
||||
FilterWithName("sandrae", NewTermQuery("user", "sandrae"))
|
||||
builder = builder.Aggregation("countByUser2", countByUserAgg2)
|
||||
}
|
||||
if esversion >= "2.0" {
|
||||
// AvgBucket
|
||||
dateHisto := NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("avgBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("avgSumOfRetweets", NewAvgBucketAggregation().BucketsPath("avgBucketDateHisto>sumOfRetweets"))
|
||||
// MinBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("minBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("minBucketSumOfRetweets", NewMinBucketAggregation().BucketsPath("minBucketDateHisto>sumOfRetweets"))
|
||||
// MaxBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("maxBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("maxBucketSumOfRetweets", NewMaxBucketAggregation().BucketsPath("maxBucketDateHisto>sumOfRetweets"))
|
||||
// SumBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("sumBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("sumBucketSumOfRetweets", NewSumBucketAggregation().BucketsPath("sumBucketDateHisto>sumOfRetweets"))
|
||||
// MovAvg
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
dateHisto = dateHisto.SubAggregation("movingAvg", NewMovAvgAggregation().BucketsPath("sumOfRetweets"))
|
||||
builder = builder.Aggregation("movingAvgDateHisto", dateHisto)
|
||||
}
|
||||
// Unnamed filters
|
||||
countByUserAgg := NewFiltersAggregation().
|
||||
Filters(NewTermQuery("user", "olivere"), NewTermQuery("user", "sandrae"))
|
||||
builder = builder.Aggregation("countByUser", countByUserAgg)
|
||||
// Named filters
|
||||
countByUserAgg2 := NewFiltersAggregation().
|
||||
FilterWithName("olivere", NewTermQuery("user", "olivere")).
|
||||
FilterWithName("sandrae", NewTermQuery("user", "sandrae"))
|
||||
builder = builder.Aggregation("countByUser2", countByUserAgg2)
|
||||
// AvgBucket
|
||||
dateHisto := NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("avgBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("avgSumOfRetweets", NewAvgBucketAggregation().BucketsPath("avgBucketDateHisto>sumOfRetweets"))
|
||||
// MinBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("minBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("minBucketSumOfRetweets", NewMinBucketAggregation().BucketsPath("minBucketDateHisto>sumOfRetweets"))
|
||||
// MaxBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("maxBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("maxBucketSumOfRetweets", NewMaxBucketAggregation().BucketsPath("maxBucketDateHisto>sumOfRetweets"))
|
||||
// SumBucket
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
builder = builder.Aggregation("sumBucketDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("sumBucketSumOfRetweets", NewSumBucketAggregation().BucketsPath("sumBucketDateHisto>sumOfRetweets"))
|
||||
// MovAvg
|
||||
dateHisto = NewDateHistogramAggregation().Field("created").Interval("year")
|
||||
dateHisto = dateHisto.SubAggregation("sumOfRetweets", NewSumAggregation().Field("retweets"))
|
||||
dateHisto = dateHisto.SubAggregation("movingAvg", NewMovAvgAggregation().BucketsPath("sumOfRetweets"))
|
||||
builder = builder.Aggregation("movingAvgDateHisto", dateHisto)
|
||||
builder = builder.Aggregation("composite", composite)
|
||||
searchResult, err := builder.Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -308,26 +310,24 @@ func TestAggs(t *testing.T) {
|
||||
}
|
||||
|
||||
// avgRetweetsWithMeta
|
||||
if esversion >= "2.0" {
|
||||
avgMetaAggRes, found := agg.Avg("avgRetweetsWithMeta")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if avgMetaAggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if avgMetaAggRes.Meta == nil {
|
||||
t.Fatalf("expected != nil; got: %v", avgMetaAggRes.Meta)
|
||||
}
|
||||
metaDataValue, found := avgMetaAggRes.Meta["meta"]
|
||||
if !found {
|
||||
t.Fatalf("expected to return meta data key %q; got: %v", "meta", found)
|
||||
}
|
||||
if flag, ok := metaDataValue.(bool); !ok {
|
||||
t.Fatalf("expected to return meta data key type %T; got: %T", true, metaDataValue)
|
||||
} else if flag != true {
|
||||
t.Fatalf("expected to return meta data key value %v; got: %v", true, flag)
|
||||
}
|
||||
avgMetaAggRes, found := agg.Avg("avgRetweetsWithMeta")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if avgMetaAggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if avgMetaAggRes.Meta == nil {
|
||||
t.Fatalf("expected != nil; got: %v", avgMetaAggRes.Meta)
|
||||
}
|
||||
metaDataValue, found := avgMetaAggRes.Meta["meta"]
|
||||
if !found {
|
||||
t.Fatalf("expected to return meta data key %q; got: %v", "meta", found)
|
||||
}
|
||||
if flag, ok := metaDataValue.(bool); !ok {
|
||||
t.Fatalf("expected to return meta data key type %T; got: %T", true, metaDataValue)
|
||||
} else if flag != true {
|
||||
t.Fatalf("expected to return meta data key value %v; got: %v", true, flag)
|
||||
}
|
||||
|
||||
// minRetweets
|
||||
@@ -817,13 +817,11 @@ func TestAggs(t *testing.T) {
|
||||
if topTags == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if esversion >= "1.4.0" {
|
||||
if topTags.DocCountErrorUpperBound != 0 {
|
||||
t.Errorf("expected %v; got: %v", 0, topTags.DocCountErrorUpperBound)
|
||||
}
|
||||
if topTags.SumOfOtherDocCount != 1 {
|
||||
t.Errorf("expected %v; got: %v", 1, topTags.SumOfOtherDocCount)
|
||||
}
|
||||
if topTags.DocCountErrorUpperBound != 0 {
|
||||
t.Errorf("expected %v; got: %v", 0, topTags.DocCountErrorUpperBound)
|
||||
}
|
||||
if topTags.SumOfOtherDocCount != 1 {
|
||||
t.Errorf("expected %v; got: %v", 1, topTags.SumOfOtherDocCount)
|
||||
}
|
||||
if len(topTags.Buckets) != 3 {
|
||||
t.Fatalf("expected %d; got: %d", 3, len(topTags.Buckets))
|
||||
@@ -924,62 +922,71 @@ func TestAggs(t *testing.T) {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
|
||||
if esversion >= "1.4" {
|
||||
// Filters agg "countByUser" (unnamed)
|
||||
countByUserAggRes, found := agg.Filters("countByUser")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if countByUserAggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if len(countByUserAggRes.Buckets) != 2 {
|
||||
t.Fatalf("expected %d; got: %d", 2, len(countByUserAggRes.Buckets))
|
||||
}
|
||||
if len(countByUserAggRes.NamedBuckets) != 0 {
|
||||
t.Fatalf("expected %d; got: %d", 0, len(countByUserAggRes.NamedBuckets))
|
||||
}
|
||||
if countByUserAggRes.Buckets[0].DocCount != 2 {
|
||||
t.Errorf("expected %d; got: %d", 2, countByUserAggRes.Buckets[0].DocCount)
|
||||
}
|
||||
if countByUserAggRes.Buckets[1].DocCount != 1 {
|
||||
t.Errorf("expected %d; got: %d", 1, countByUserAggRes.Buckets[1].DocCount)
|
||||
}
|
||||
// Filters agg "countByUser" (unnamed)
|
||||
countByUserAggRes, found := agg.Filters("countByUser")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if countByUserAggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if len(countByUserAggRes.Buckets) != 2 {
|
||||
t.Fatalf("expected %d; got: %d", 2, len(countByUserAggRes.Buckets))
|
||||
}
|
||||
if len(countByUserAggRes.NamedBuckets) != 0 {
|
||||
t.Fatalf("expected %d; got: %d", 0, len(countByUserAggRes.NamedBuckets))
|
||||
}
|
||||
if countByUserAggRes.Buckets[0].DocCount != 2 {
|
||||
t.Errorf("expected %d; got: %d", 2, countByUserAggRes.Buckets[0].DocCount)
|
||||
}
|
||||
if countByUserAggRes.Buckets[1].DocCount != 1 {
|
||||
t.Errorf("expected %d; got: %d", 1, countByUserAggRes.Buckets[1].DocCount)
|
||||
}
|
||||
|
||||
// Filters agg "countByUser2" (named)
|
||||
countByUser2AggRes, found := agg.Filters("countByUser2")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if countByUser2AggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if len(countByUser2AggRes.Buckets) != 0 {
|
||||
t.Fatalf("expected %d; got: %d", 0, len(countByUser2AggRes.Buckets))
|
||||
}
|
||||
if len(countByUser2AggRes.NamedBuckets) != 2 {
|
||||
t.Fatalf("expected %d; got: %d", 2, len(countByUser2AggRes.NamedBuckets))
|
||||
}
|
||||
b, found := countByUser2AggRes.NamedBuckets["olivere"]
|
||||
if !found {
|
||||
t.Fatalf("expected bucket %q; got: %v", "olivere", found)
|
||||
}
|
||||
if b == nil {
|
||||
t.Fatalf("expected bucket %q; got: %v", "olivere", b)
|
||||
}
|
||||
if b.DocCount != 2 {
|
||||
t.Errorf("expected %d; got: %d", 2, b.DocCount)
|
||||
}
|
||||
b, found = countByUser2AggRes.NamedBuckets["sandrae"]
|
||||
if !found {
|
||||
t.Fatalf("expected bucket %q; got: %v", "sandrae", found)
|
||||
}
|
||||
if b == nil {
|
||||
t.Fatalf("expected bucket %q; got: %v", "sandrae", b)
|
||||
}
|
||||
if b.DocCount != 1 {
|
||||
t.Errorf("expected %d; got: %d", 1, b.DocCount)
|
||||
}
|
||||
// Filters agg "countByUser2" (named)
|
||||
countByUser2AggRes, found := agg.Filters("countByUser2")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if countByUser2AggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if len(countByUser2AggRes.Buckets) != 0 {
|
||||
t.Fatalf("expected %d; got: %d", 0, len(countByUser2AggRes.Buckets))
|
||||
}
|
||||
if len(countByUser2AggRes.NamedBuckets) != 2 {
|
||||
t.Fatalf("expected %d; got: %d", 2, len(countByUser2AggRes.NamedBuckets))
|
||||
}
|
||||
b, found := countByUser2AggRes.NamedBuckets["olivere"]
|
||||
if !found {
|
||||
t.Fatalf("expected bucket %q; got: %v", "olivere", found)
|
||||
}
|
||||
if b == nil {
|
||||
t.Fatalf("expected bucket %q; got: %v", "olivere", b)
|
||||
}
|
||||
if b.DocCount != 2 {
|
||||
t.Errorf("expected %d; got: %d", 2, b.DocCount)
|
||||
}
|
||||
b, found = countByUser2AggRes.NamedBuckets["sandrae"]
|
||||
if !found {
|
||||
t.Fatalf("expected bucket %q; got: %v", "sandrae", found)
|
||||
}
|
||||
if b == nil {
|
||||
t.Fatalf("expected bucket %q; got: %v", "sandrae", b)
|
||||
}
|
||||
if b.DocCount != 1 {
|
||||
t.Errorf("expected %d; got: %d", 1, b.DocCount)
|
||||
}
|
||||
|
||||
compositeAggRes, found := agg.Composite("composite")
|
||||
if !found {
|
||||
t.Errorf("expected %v; got: %v", true, found)
|
||||
}
|
||||
if compositeAggRes == nil {
|
||||
t.Fatalf("expected != nil; got: nil")
|
||||
}
|
||||
if want, have := 3, len(compositeAggRes.Buckets); want != have {
|
||||
t.Fatalf("expected %d; got: %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3231,3 +3238,179 @@ func TestAggsPipelineSerialDiff(t *testing.T) {
|
||||
t.Fatalf("expected aggregation value = %v; got: %v", float64(20), *agg.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggsComposite(t *testing.T) {
|
||||
s := `{
|
||||
"the_composite" : {
|
||||
"buckets" : [
|
||||
{
|
||||
"key" : {
|
||||
"composite_users" : "olivere",
|
||||
"composite_retweets" : 0.0,
|
||||
"composite_created" : 1349856720000
|
||||
},
|
||||
"doc_count" : 1
|
||||
},
|
||||
{
|
||||
"key" : {
|
||||
"composite_users" : "olivere",
|
||||
"composite_retweets" : 108.0,
|
||||
"composite_created" : 1355333880000
|
||||
},
|
||||
"doc_count" : 1
|
||||
},
|
||||
{
|
||||
"key" : {
|
||||
"composite_users" : "sandrae",
|
||||
"composite_retweets" : 12.0,
|
||||
"composite_created" : 1321009080000
|
||||
},
|
||||
"doc_count" : 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
aggs := new(Aggregations)
|
||||
err := json.Unmarshal([]byte(s), &aggs)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error decoding; got: %v", err)
|
||||
}
|
||||
|
||||
agg, found := aggs.Composite("the_composite")
|
||||
if !found {
|
||||
t.Fatalf("expected aggregation to be found; got: %v", found)
|
||||
}
|
||||
if agg == nil {
|
||||
t.Fatalf("expected aggregation != nil; got: %v", agg)
|
||||
}
|
||||
if want, have := 3, len(agg.Buckets); want != have {
|
||||
t.Fatalf("expected aggregation buckets length = %v; got: %v", want, have)
|
||||
}
|
||||
|
||||
// 1st bucket
|
||||
bucket := agg.Buckets[0]
|
||||
if want, have := int64(1), bucket.DocCount; want != have {
|
||||
t.Fatalf("expected aggregation bucket doc count = %v; got: %v", want, have)
|
||||
}
|
||||
if want, have := 3, len(bucket.Key); want != have {
|
||||
t.Fatalf("expected aggregation bucket key length = %v; got: %v", want, have)
|
||||
}
|
||||
v, found := bucket.Key["composite_users"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_users")
|
||||
}
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := "olivere", s; want != have {
|
||||
t.Fatalf("expected to find bucket key value %q; got: %q", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_retweets"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_retweets")
|
||||
}
|
||||
f, ok := v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 0.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_created"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_created")
|
||||
}
|
||||
f, ok = v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 1349856720000.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
|
||||
// 2nd bucket
|
||||
bucket = agg.Buckets[1]
|
||||
if want, have := int64(1), bucket.DocCount; want != have {
|
||||
t.Fatalf("expected aggregation bucket doc count = %v; got: %v", want, have)
|
||||
}
|
||||
if want, have := 3, len(bucket.Key); want != have {
|
||||
t.Fatalf("expected aggregation bucket key length = %v; got: %v", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_users"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_users")
|
||||
}
|
||||
s, ok = v.(string)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := "olivere", s; want != have {
|
||||
t.Fatalf("expected to find bucket key value %q; got: %q", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_retweets"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_retweets")
|
||||
}
|
||||
f, ok = v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 108.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_created"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_created")
|
||||
}
|
||||
f, ok = v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 1355333880000.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
|
||||
// 3rd bucket
|
||||
bucket = agg.Buckets[2]
|
||||
if want, have := int64(1), bucket.DocCount; want != have {
|
||||
t.Fatalf("expected aggregation bucket doc count = %v; got: %v", want, have)
|
||||
}
|
||||
if want, have := 3, len(bucket.Key); want != have {
|
||||
t.Fatalf("expected aggregation bucket key length = %v; got: %v", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_users"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_users")
|
||||
}
|
||||
s, ok = v.(string)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := "sandrae", s; want != have {
|
||||
t.Fatalf("expected to find bucket key value %q; got: %q", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_retweets"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_retweets")
|
||||
}
|
||||
f, ok = v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 12.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
v, found = bucket.Key["composite_created"]
|
||||
if !found {
|
||||
t.Fatalf("expected to find bucket key %q", "composite_created")
|
||||
}
|
||||
f, ok = v.(float64)
|
||||
if !ok {
|
||||
t.Fatalf("expected to have bucket key of type string; got: %T", v)
|
||||
}
|
||||
if want, have := 1321009080000.0, f; want != have {
|
||||
t.Fatalf("expected to find bucket key value %v; got: %v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
96
vendor/github.com/olivere/elastic/search_queries_terms_set.go
сгенерированный
поставляемый
Обычный файл
96
vendor/github.com/olivere/elastic/search_queries_terms_set.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,96 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
// TermsSetQuery returns any documents that match with at least
|
||||
// one or more of the provided terms. The terms are not analyzed
|
||||
// and thus must match exactly. The number of terms that must
|
||||
// match varies per document and is either controlled by a
|
||||
// minimum should match field or computed per document in a
|
||||
// minimum should match script.
|
||||
//
|
||||
// For more details, see
|
||||
// https://www.elastic.co/guide/en/elasticsearch/reference/6.1/query-dsl-terms-set-query.html
|
||||
type TermsSetQuery struct {
|
||||
name string
|
||||
values []interface{}
|
||||
minimumShouldMatchField string
|
||||
minimumShouldMatchScript *Script
|
||||
queryName string
|
||||
boost *float64
|
||||
}
|
||||
|
||||
// NewTermsSetQuery creates and initializes a new TermsSetQuery.
|
||||
func NewTermsSetQuery(name string, values ...interface{}) *TermsSetQuery {
|
||||
q := &TermsSetQuery{
|
||||
name: name,
|
||||
}
|
||||
if len(values) > 0 {
|
||||
q.values = append(q.values, values...)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// MinimumShouldMatchField specifies the field to match.
|
||||
func (q *TermsSetQuery) MinimumShouldMatchField(minimumShouldMatchField string) *TermsSetQuery {
|
||||
q.minimumShouldMatchField = minimumShouldMatchField
|
||||
return q
|
||||
}
|
||||
|
||||
// MinimumShouldMatchScript specifies the script to match.
|
||||
func (q *TermsSetQuery) MinimumShouldMatchScript(minimumShouldMatchScript *Script) *TermsSetQuery {
|
||||
q.minimumShouldMatchScript = minimumShouldMatchScript
|
||||
return q
|
||||
}
|
||||
|
||||
// Boost sets the boost for this query.
|
||||
func (q *TermsSetQuery) Boost(boost float64) *TermsSetQuery {
|
||||
q.boost = &boost
|
||||
return q
|
||||
}
|
||||
|
||||
// QueryName sets the query name for the filter that can be used
|
||||
// when searching for matched_filters per hit
|
||||
func (q *TermsSetQuery) QueryName(queryName string) *TermsSetQuery {
|
||||
q.queryName = queryName
|
||||
return q
|
||||
}
|
||||
|
||||
// Source creates the query source for the term query.
|
||||
func (q *TermsSetQuery) Source() (interface{}, error) {
|
||||
// {"terms_set":{"codes":{"terms":["abc","def"],"minimum_should_match_field":"required_matches"}}}
|
||||
source := make(map[string]interface{})
|
||||
inner := make(map[string]interface{})
|
||||
params := make(map[string]interface{})
|
||||
inner[q.name] = params
|
||||
source["terms_set"] = inner
|
||||
|
||||
// terms
|
||||
params["terms"] = q.values
|
||||
|
||||
// minimum_should_match_field
|
||||
if match := q.minimumShouldMatchField; match != "" {
|
||||
params["minimum_should_match_field"] = match
|
||||
}
|
||||
|
||||
// minimum_should_match_script
|
||||
if match := q.minimumShouldMatchScript; match != nil {
|
||||
src, err := match.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params["minimum_should_match_script"] = src
|
||||
}
|
||||
|
||||
// Common parameters for all queries
|
||||
if q.boost != nil {
|
||||
params["boost"] = *q.boost
|
||||
}
|
||||
if q.queryName != "" {
|
||||
params["_name"] = q.queryName
|
||||
}
|
||||
|
||||
return source, nil
|
||||
}
|
||||
75
vendor/github.com/olivere/elastic/search_queries_terms_set_test.go
сгенерированный
поставляемый
Обычный файл
75
vendor/github.com/olivere/elastic/search_queries_terms_set_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,75 @@
|
||||
// Copyright 2012-present Oliver Eilhard. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-license.
|
||||
// See http://olivere.mit-license.org/license.txt for details.
|
||||
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTermsSetQueryWithField(t *testing.T) {
|
||||
q := NewTermsSetQuery("codes", "abc", "def", "ghi").MinimumShouldMatchField("required_matches")
|
||||
src, err := q.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"terms_set":{"codes":{"minimum_should_match_field":"required_matches","terms":["abc","def","ghi"]}}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTermsSetQueryWithScript(t *testing.T) {
|
||||
q := NewTermsSetQuery("codes", "abc", "def", "ghi").
|
||||
MinimumShouldMatchScript(
|
||||
NewScript(`Math.min(params.num_terms, doc['required_matches'].value)`),
|
||||
)
|
||||
src, err := q.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON failed: %v", err)
|
||||
}
|
||||
got := string(data)
|
||||
expected := `{"terms_set":{"codes":{"minimum_should_match_script":{"source":"Math.min(params.num_terms, doc['required_matches'].value)"},"terms":["abc","def","ghi"]}}}`
|
||||
if got != expected {
|
||||
t.Errorf("expected\n%s\n,got:\n%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchTermsSetQuery(t *testing.T) {
|
||||
//client := setupTestClientAndCreateIndexAndAddDocs(t, SetTraceLog(log.New(os.Stdout, "", log.LstdFlags)))
|
||||
client := setupTestClientAndCreateIndexAndAddDocs(t)
|
||||
|
||||
// Match all should return all documents
|
||||
searchResult, err := client.Search().
|
||||
Index(testIndexName).
|
||||
Query(
|
||||
NewTermsSetQuery("user", "olivere", "sandrae").
|
||||
MinimumShouldMatchField("retweets"),
|
||||
).
|
||||
Pretty(true).
|
||||
Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if searchResult.Hits == nil {
|
||||
t.Errorf("expected SearchResult.Hits != nil; got nil")
|
||||
}
|
||||
if got, want := searchResult.Hits.TotalHits, int64(3); got != want {
|
||||
t.Errorf("expected SearchResult.Hits.TotalHits = %d; got %d", want, got)
|
||||
}
|
||||
if got, want := len(searchResult.Hits.Hits), 3; got != want {
|
||||
t.Errorf("expected len(SearchResult.Hits.Hits) = %d; got %d", want, got)
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/olivere/elastic/search_request.go
сгенерированный
поставляемый
74
vendor/github.com/olivere/elastic/search_request.go
сгенерированный
поставляемый
@@ -4,13 +4,16 @@
|
||||
|
||||
package elastic
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchRequest combines a search request and its
|
||||
// query details (see SearchSource).
|
||||
// It is used in combination with MultiSearch.
|
||||
type SearchRequest struct {
|
||||
searchType string // default in ES is "query_then_fetch"
|
||||
searchType string
|
||||
indices []string
|
||||
types []string
|
||||
routing *string
|
||||
@@ -28,38 +31,23 @@ func NewSearchRequest() *SearchRequest {
|
||||
return &SearchRequest{}
|
||||
}
|
||||
|
||||
// SearchRequest must be one of "query_then_fetch", "query_and_fetch",
|
||||
// "scan", "count", "dfs_query_then_fetch", or "dfs_query_and_fetch".
|
||||
// Use one of the constants defined via SearchType.
|
||||
// SearchRequest must be one of "dfs_query_then_fetch" or
|
||||
// "query_then_fetch".
|
||||
func (r *SearchRequest) SearchType(searchType string) *SearchRequest {
|
||||
r.searchType = searchType
|
||||
return r
|
||||
}
|
||||
|
||||
// SearchTypeDfsQueryThenFetch sets search type to dfs_query_then_fetch.
|
||||
func (r *SearchRequest) SearchTypeDfsQueryThenFetch() *SearchRequest {
|
||||
return r.SearchType("dfs_query_then_fetch")
|
||||
}
|
||||
|
||||
func (r *SearchRequest) SearchTypeDfsQueryAndFetch() *SearchRequest {
|
||||
return r.SearchType("dfs_query_and_fetch")
|
||||
}
|
||||
|
||||
// SearchTypeQueryThenFetch sets search type to query_then_fetch.
|
||||
func (r *SearchRequest) SearchTypeQueryThenFetch() *SearchRequest {
|
||||
return r.SearchType("query_then_fetch")
|
||||
}
|
||||
|
||||
func (r *SearchRequest) SearchTypeQueryAndFetch() *SearchRequest {
|
||||
return r.SearchType("query_and_fetch")
|
||||
}
|
||||
|
||||
func (r *SearchRequest) SearchTypeScan() *SearchRequest {
|
||||
return r.SearchType("scan")
|
||||
}
|
||||
|
||||
func (r *SearchRequest) SearchTypeCount() *SearchRequest {
|
||||
return r.SearchType("count")
|
||||
}
|
||||
|
||||
func (r *SearchRequest) Index(indices ...string) *SearchRequest {
|
||||
r.indices = append(r.indices, indices...)
|
||||
return r
|
||||
@@ -130,17 +118,7 @@ func (r *SearchRequest) SearchSource(searchSource *SearchSource) *SearchRequest
|
||||
}
|
||||
|
||||
func (r *SearchRequest) Source(source interface{}) *SearchRequest {
|
||||
switch v := source.(type) {
|
||||
case *SearchSource:
|
||||
src, err := v.Source()
|
||||
if err != nil {
|
||||
// Do not do anything in case of an error
|
||||
return r
|
||||
}
|
||||
r.source = src
|
||||
default:
|
||||
r.source = source
|
||||
}
|
||||
r.source = source
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -200,6 +178,34 @@ func (r *SearchRequest) header() interface{} {
|
||||
// Body is used e.g. by MultiSearch to get information about the search body
|
||||
// of one SearchRequest.
|
||||
// See https://www.elastic.co/guide/en/elasticsearch/reference/6.0/search-multi-search.html
|
||||
func (r *SearchRequest) Body() interface{} {
|
||||
return r.source
|
||||
func (r *SearchRequest) Body() (string, error) {
|
||||
switch t := r.source.(type) {
|
||||
default:
|
||||
body, err := json.Marshal(r.source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
case *SearchSource:
|
||||
src, err := t.Source()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body, err := json.Marshal(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
case json.RawMessage:
|
||||
return string(t), nil
|
||||
case *json.RawMessage:
|
||||
return string(*t), nil
|
||||
case string:
|
||||
return t, nil
|
||||
case *string:
|
||||
if t != nil {
|
||||
return *t, nil
|
||||
}
|
||||
return "{}", nil
|
||||
}
|
||||
}
|
||||
|
||||
55
vendor/github.com/olivere/elastic/search_test.go
сгенерированный
поставляемый
55
vendor/github.com/olivere/elastic/search_test.go
сгенерированный
поставляемый
@@ -607,6 +607,61 @@ func TestSearchSource(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchSourceWithString(t *testing.T) {
|
||||
client := setupTestClientAndCreateIndex(t)
|
||||
|
||||
tweet1 := tweet{
|
||||
User: "olivere", Retweets: 108,
|
||||
Message: "Welcome to Golang and Elasticsearch.",
|
||||
Created: time.Date(2012, 12, 12, 17, 38, 34, 0, time.UTC),
|
||||
}
|
||||
tweet2 := tweet{
|
||||
User: "olivere", Retweets: 0,
|
||||
Message: "Another unrelated topic.",
|
||||
Created: time.Date(2012, 10, 10, 8, 12, 03, 0, time.UTC),
|
||||
}
|
||||
tweet3 := tweet{
|
||||
User: "sandrae", Retweets: 12,
|
||||
Message: "Cycling is fun.",
|
||||
Created: time.Date(2011, 11, 11, 10, 58, 12, 0, time.UTC),
|
||||
}
|
||||
|
||||
// Add all documents
|
||||
_, err := client.Index().Index(testIndexName).Type("doc").Id("1").BodyJson(&tweet1).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Index().Index(testIndexName).Type("doc").Id("2").BodyJson(&tweet2).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Index().Index(testIndexName).Type("doc").Id("3").BodyJson(&tweet3).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.Flush().Index(testIndexName).Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
searchResult, err := client.Search().
|
||||
Index(testIndexName).
|
||||
Source(`{"query":{"match_all":{}}}`). // sets the JSON request
|
||||
Do(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if searchResult.Hits == nil {
|
||||
t.Errorf("expected SearchResult.Hits != nil; got nil")
|
||||
}
|
||||
if searchResult.Hits.TotalHits != 3 {
|
||||
t.Errorf("expected SearchResult.Hits.TotalHits = %d; got %d", 3, searchResult.Hits.TotalHits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchRawString(t *testing.T) {
|
||||
// client := setupTestClientAndCreateIndexAndLog(t, SetTraceLog(log.New(os.Stdout, "", 0)))
|
||||
client := setupTestClientAndCreateIndex(t)
|
||||
|
||||
8
vendor/github.com/prometheus/client_golang/ISSUE_TEMPLATE.md
сгенерированный
поставляемый
Обычный файл
8
vendor/github.com/prometheus/client_golang/ISSUE_TEMPLATE.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
<!--
|
||||
|
||||
If you are unhappy how your favorite Go dependency management tool deals
|
||||
with this repository, please do not file an issue but read
|
||||
https://github.com/prometheus/client_golang#important-note-about-releases-versioning-tagging-stability-and-your-favorite-go-dependency-management-tool
|
||||
instead. Thank you very much.
|
||||
|
||||
-->
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user