mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-27535] User invite limits for MM Cloud (#15197)
* Add a config for MM User Limit * Adding graceful errors for if an administrator invites people passed their user limit * Including changed vendor files * Adding unit test * Fix a bug * Push up working tests (Thanks Joram) * Add more cases, clean up logs in code * One more case * Refactoring based on PR comments * Updating i18n * Some changes based on PR review * Remove a comment * Bring back some translations that were somehow removed Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
17
api4/team.go
17
api4/team.go
@@ -1177,6 +1177,7 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
emailList := model.ArrayFromJson(r.Body)
|
||||
|
||||
for i := range emailList {
|
||||
emailList[i] = strings.ToLower(emailList[i])
|
||||
}
|
||||
@@ -1193,7 +1194,21 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("emails", emailList)
|
||||
|
||||
if graceful {
|
||||
invitesWithError, err := c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId)
|
||||
cloudUserLimit := *c.App.Config().ExperimentalSettings.CloudUserLimit
|
||||
var invitesOverLimit []*model.EmailInviteWithError
|
||||
if cloudUserLimit > 0 && c.IsSystemAdmin() {
|
||||
emailList, invitesOverLimit, _ = c.App.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit)
|
||||
}
|
||||
var invitesWithError []*model.EmailInviteWithError
|
||||
var err *model.AppError
|
||||
if emailList != nil {
|
||||
invitesWithError, err = c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.App.Session().UserId)
|
||||
}
|
||||
|
||||
if len(invitesOverLimit) > 0 {
|
||||
invitesWithError = append(invitesWithError, invitesOverLimit...)
|
||||
}
|
||||
|
||||
if invitesWithError != nil {
|
||||
errList := make([]string, 0, len(invitesWithError))
|
||||
for _, inv := range invitesWithError {
|
||||
|
||||
@@ -2721,6 +2721,69 @@ func TestImportTeam(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestInviteUsersToTeamWithUserLimit(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
email1 := th.GenerateTestEmail()
|
||||
email2 := th.GenerateTestEmail()
|
||||
email3 := th.GenerateTestEmail()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnableEmailInvitations = true
|
||||
*cfg.ExperimentalSettings.CloudUserLimit = 2
|
||||
})
|
||||
|
||||
t.Run("System admin, invite when at limit should fail", func(t *testing.T) {
|
||||
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, invitesWithErrors, 2)
|
||||
require.NotNil(t, invitesWithErrors[0].Error)
|
||||
assert.Equal(t, invitesWithErrors[0].Error.Message, "You've reached the user limit of your current tier")
|
||||
require.NotNil(t, invitesWithErrors[1].Error)
|
||||
assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier")
|
||||
})
|
||||
|
||||
t.Run("Regular user, invite when at limit should succeed", func(t *testing.T) {
|
||||
invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email3})
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, invitesWithErrors, 1)
|
||||
assert.Nil(t, invitesWithErrors[0].Error)
|
||||
|
||||
})
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ExperimentalSettings.CloudUserLimit = 5
|
||||
})
|
||||
|
||||
t.Run("With one remaining user inviting more than one user as admin invites only one user", func(t *testing.T) {
|
||||
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, invitesWithErrors, 2)
|
||||
require.Nil(t, invitesWithErrors[0].Error)
|
||||
require.NotNil(t, invitesWithErrors[1].Error)
|
||||
assert.Equal(t, invitesWithErrors[1].Error.Message, "You've reached the user limit of your current tier")
|
||||
|
||||
})
|
||||
|
||||
t.Run("With one remaining user inviting more than one user as a regular user sends all invites", func(t *testing.T) {
|
||||
invitesWithErrors, resp := th.Client.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, invitesWithErrors, 2)
|
||||
assert.Nil(t, invitesWithErrors[0].Error)
|
||||
assert.Nil(t, invitesWithErrors[1].Error)
|
||||
|
||||
})
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ExperimentalSettings.CloudUserLimit = 100
|
||||
})
|
||||
t.Run("Invited user count is well below limit", func(t *testing.T) {
|
||||
invitesWithErrors, resp := th.SystemAdminClient.InviteUsersToTeamGracefully(th.BasicTeam.Id, []string{email1, email2})
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, invitesWithErrors, 2)
|
||||
require.Nil(t, invitesWithErrors[0].Error)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInviteUsersToTeam(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -537,6 +537,7 @@ type AppIface interface {
|
||||
GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError)
|
||||
GetEmojiImage(emojiId string) ([]byte, string, *model.AppError)
|
||||
GetEmojiList(page, perPage int, sort string) ([]*model.Emoji, *model.AppError)
|
||||
GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError)
|
||||
GetFile(fileId string) ([]byte, *model.AppError)
|
||||
GetFileInfo(fileId string) (*model.FileInfo, *model.AppError)
|
||||
GetFileInfos(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError)
|
||||
|
||||
@@ -5069,6 +5069,28 @@ func (a *OpenTracingAppLayer) GetEnvironmentConfig() map[string]interface{} {
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetErrorListForEmailsOverLimit")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1, resultVar2 := a.app.GetErrorListForEmailsOverLimit(emailList, cloudUserLimit)
|
||||
|
||||
if resultVar2 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar2))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1, resultVar2
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetFile(fileId string) ([]byte, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFile")
|
||||
|
||||
42
app/team.go
42
app/team.go
@@ -1127,6 +1127,47 @@ func (a *App) prepareInviteNewUsersToTeam(teamId, senderId string) (*model.User,
|
||||
return user, team, nil
|
||||
}
|
||||
|
||||
func genEmailInviteWithErrorList(emailList []string) []*model.EmailInviteWithError {
|
||||
invitesNotSent := make([]*model.EmailInviteWithError, len(emailList))
|
||||
for i := range emailList {
|
||||
invite := &model.EmailInviteWithError{
|
||||
Email: emailList[i],
|
||||
Error: model.NewAppError("inviteUsersToTeam", "api.team.invite_members.limit_reached.app_error", map[string]interface{}{"Addresses": emailList[i]}, "", http.StatusBadRequest),
|
||||
}
|
||||
invitesNotSent[i] = invite
|
||||
}
|
||||
return invitesNotSent
|
||||
}
|
||||
|
||||
func (a *App) GetErrorListForEmailsOverLimit(emailList []string, cloudUserLimit int64) ([]string, []*model.EmailInviteWithError, *model.AppError) {
|
||||
var invitesNotSent []*model.EmailInviteWithError
|
||||
if cloudUserLimit <= 0 {
|
||||
return emailList, invitesNotSent, nil
|
||||
}
|
||||
systemUserCount, _ := a.Srv().Store.User().Count(model.UserCountOptions{})
|
||||
remainingUsers := cloudUserLimit - systemUserCount
|
||||
if remainingUsers <= 0 {
|
||||
// No remaining users so all fail
|
||||
invitesNotSent = genEmailInviteWithErrorList(emailList)
|
||||
emailList = nil
|
||||
} else if remainingUsers < int64(len(emailList)) {
|
||||
// Trim the email list to only invite as many users as are remaining in subscription
|
||||
// Set graceful errors for the remaining email addresses
|
||||
emailsAboveLimit := emailList[remainingUsers:]
|
||||
invitesNotSent = genEmailInviteWithErrorList(emailsAboveLimit)
|
||||
// If 1 user remaining we have to prevent 0:0 reslicing
|
||||
if remainingUsers == 1 {
|
||||
email := emailList[0]
|
||||
emailList = nil
|
||||
emailList = append(emailList, email)
|
||||
} else {
|
||||
emailList = emailList[:(remainingUsers - 1)]
|
||||
}
|
||||
}
|
||||
|
||||
return emailList, invitesNotSent, nil
|
||||
}
|
||||
|
||||
func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderId string) ([]*model.EmailInviteWithError, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableEmailInvitations {
|
||||
return nil, model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
@@ -1136,7 +1177,6 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamId, senderI
|
||||
err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.no_one.app_error", nil, "", http.StatusBadRequest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, team, err := a.prepareInviteNewUsersToTeam(teamId, senderId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -52,6 +52,7 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L
|
||||
props["ExperimentalEnablePostMetadata"] = "true"
|
||||
props["ExperimentalEnableClickToReply"] = strconv.FormatBool(*c.ExperimentalSettings.EnableClickToReply)
|
||||
|
||||
props["ExperimentalCloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 8)
|
||||
if *c.ServiceSettings.ExperimentalChannelOrganization || *c.ServiceSettings.ExperimentalGroupUnreadChannels != model.GROUP_UNREAD_CHANNELS_DISABLED {
|
||||
props["ExperimentalChannelOrganization"] = strconv.FormatBool(true)
|
||||
} else {
|
||||
@@ -96,6 +97,8 @@ func GenerateClientConfig(c *model.Config, diagnosticID string, license *model.L
|
||||
|
||||
props["EnableEmailInvitations"] = strconv.FormatBool(*c.ServiceSettings.EnableEmailInvitations)
|
||||
|
||||
props["CloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 10)
|
||||
|
||||
// Set default values for all options that require a license.
|
||||
props["ExperimentalHideTownSquareinLHS"] = "false"
|
||||
props["ExperimentalTownSquareIsReadOnly"] = "false"
|
||||
|
||||
8
go.mod
8
go.mod
@@ -85,7 +85,7 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/rudderlabs/analytics-go v3.2.1+incompatible
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
|
||||
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
@@ -108,6 +108,9 @@ require (
|
||||
github.com/wiggin77/merror v1.0.2
|
||||
github.com/wiggin77/srslog v1.0.1
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
go.uber.org/zap v1.15.0
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
golang.org/x/image v0.0.0-20200618115811-c13761719519
|
||||
@@ -116,7 +119,8 @@ require (
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd // indirect
|
||||
golang.org/x/text v0.3.3
|
||||
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f
|
||||
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5 // indirect
|
||||
google.golang.org/grpc v1.30.0 // indirect
|
||||
|
||||
9
go.sum
9
go.sum
@@ -760,8 +760,11 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a h1:00UFliGZl2UciXe8o/2iuEsRQ9u7z0rzDTVzuj6EYY0=
|
||||
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
@@ -866,6 +869,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -919,6 +923,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -939,10 +944,14 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f h1:JcoF/bowzCDI+MXu1yLqQGNO3ibqWsWq+Sk7pOT218w=
|
||||
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692 h1:fsn47thVa7Ar/TMyXYlZgOoT7M4+kRpb+KpSAqRQx1w=
|
||||
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
|
||||
@@ -4,8 +4,8 @@ go 1.14
|
||||
|
||||
require (
|
||||
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // indirect
|
||||
github.com/philhofer/fwd v1.0.0 // indirect
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 // indirect
|
||||
github.com/philhofer/fwd v1.0.0 // indirect
|
||||
github.com/reflog/struct2interface v0.6.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.2 // indirect
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
|
||||
|
||||
@@ -58,6 +58,8 @@ github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200721093743-053c38dcd293/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 h1:+JXFDXEkbMieBt54Rihj3ppnZ/Pm1kZxkLOD0hrWFzs=
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292 h1:swpUQyq7HWhc0aStUAgdZI6o0UOD9OzXJxbxUTLo1fo=
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200814100246-5fc14ed48292/go.mod h1:3gKozJI8n2Y/vW37GfnFWAdehGXe5yZlt+HykK6Y3DM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
|
||||
@@ -2214,6 +2214,10 @@
|
||||
"id": "api.team.invite_members.invalid_email.app_error",
|
||||
"translation": "The following email addresses do not belong to an accepted domain: {{.Addresses}}. Please contact your System Administrator for details."
|
||||
},
|
||||
{
|
||||
"id": "api.team.invite_members.limit_reached.app_error",
|
||||
"translation": "You've reached the user limit of your current tier"
|
||||
},
|
||||
{
|
||||
"id": "api.team.invite_members.no_one.app_error",
|
||||
"translation": "No one to invite."
|
||||
|
||||
@@ -861,6 +861,7 @@ type ExperimentalSettings struct {
|
||||
EnableClickToReply *bool `restricted:"true"`
|
||||
LinkMetadataTimeoutMilliseconds *int64 `restricted:"true"`
|
||||
RestrictSystemAdmin *bool `restricted:"true"`
|
||||
CloudUserLimit *int64 `restricted:"true"`
|
||||
UseNewSAMLLibrary *bool
|
||||
}
|
||||
|
||||
@@ -884,6 +885,11 @@ func (s *ExperimentalSettings) SetDefaults() {
|
||||
if s.RestrictSystemAdmin == nil {
|
||||
s.RestrictSystemAdmin = NewBool(false)
|
||||
}
|
||||
|
||||
if s.CloudUserLimit == nil {
|
||||
// User limit 0 is treated as no limit
|
||||
s.CloudUserLimit = NewInt64(0)
|
||||
}
|
||||
if s.UseNewSAMLLibrary == nil {
|
||||
s.UseNewSAMLLibrary = NewBool(false)
|
||||
}
|
||||
|
||||
89
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
89
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
@@ -89,6 +89,10 @@ type golistState struct {
|
||||
rootDirsError error
|
||||
rootDirs map[string]string
|
||||
|
||||
goVersionOnce sync.Once
|
||||
goVersionError error
|
||||
goVersion string // third field of 'go version'
|
||||
|
||||
// vendorDirs caches the (non)existence of vendor directories.
|
||||
vendorDirs map[string]bool
|
||||
}
|
||||
@@ -635,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||
pkg.CompiledGoFiles = pkg.GoFiles
|
||||
}
|
||||
|
||||
// Temporary work-around for golang/go#39986. Parse filenames out of
|
||||
// error messages. This happens if there are unrecoverable syntax
|
||||
// errors in the source, so we can't match on a specific error message.
|
||||
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
|
||||
addFilenameFromPos := func(pos string) bool {
|
||||
split := strings.Split(pos, ":")
|
||||
if len(split) < 1 {
|
||||
return false
|
||||
}
|
||||
filename := strings.TrimSpace(split[0])
|
||||
if filename == "" {
|
||||
return false
|
||||
}
|
||||
if !filepath.IsAbs(filename) {
|
||||
filename = filepath.Join(state.cfg.Dir, filename)
|
||||
}
|
||||
info, _ := os.Stat(filename)
|
||||
if info == nil {
|
||||
return false
|
||||
}
|
||||
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
|
||||
pkg.GoFiles = append(pkg.GoFiles, filename)
|
||||
return true
|
||||
}
|
||||
found := addFilenameFromPos(err.Pos)
|
||||
// In some cases, go list only reports the error position in the
|
||||
// error text, not the error position. One such case is when the
|
||||
// file's package name is a keyword (see golang.org/issue/39763).
|
||||
if !found {
|
||||
addFilenameFromPos(err.Err)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Error != nil {
|
||||
msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
|
||||
// Address golang.org/issue/35964 by appending import stack to error message.
|
||||
@@ -664,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
|
||||
if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
goV, err := state.getGoVersion()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
|
||||
// The import stack behaves differently for these versions than newer Go versions.
|
||||
if strings.HasPrefix(goV, "go1.13") || strings.HasPrefix(goV, "go1.14") {
|
||||
return len(p.Error.ImportStack) == 0
|
||||
}
|
||||
|
||||
// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
|
||||
// or the current package is at the top of the import stack. This is not guaranteed
|
||||
// to work perfectly, but should avoid some cases where files in errors don't belong to this
|
||||
// package.
|
||||
return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
|
||||
}
|
||||
|
||||
func (state *golistState) getGoVersion() (string, error) {
|
||||
state.goVersionOnce.Do(func() {
|
||||
var b *bytes.Buffer
|
||||
// Invoke go version. Don't use invokeGo because it will supply build flags, and
|
||||
// go version doesn't expect build flags.
|
||||
inv := gocommand.Invocation{
|
||||
Verb: "version",
|
||||
Env: state.cfg.Env,
|
||||
Logf: state.cfg.Logf,
|
||||
}
|
||||
gocmdRunner := state.cfg.gocmdRunner
|
||||
if gocmdRunner == nil {
|
||||
gocmdRunner = &gocommand.Runner{}
|
||||
}
|
||||
b, _, _, state.goVersionError = gocmdRunner.RunRaw(state.cfg.Context, inv)
|
||||
if state.goVersionError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sp := strings.Split(b.String(), " ")
|
||||
if len(sp) < 3 {
|
||||
state.goVersionError = fmt.Errorf("go version output: expected 'go version <version>', got '%s'", b.String())
|
||||
return
|
||||
}
|
||||
state.goVersion = sp[2]
|
||||
})
|
||||
return state.goVersion, state.goVersionError
|
||||
}
|
||||
|
||||
// getPkgPath finds the package path of a directory if it's relative to a root directory.
|
||||
func (state *golistState) getPkgPath(dir string) (string, bool, error) {
|
||||
absDir, err := filepath.Abs(dir)
|
||||
|
||||
66
vendor/golang.org/x/tools/go/packages/golist_overlay.go
generated
vendored
66
vendor/golang.org/x/tools/go/packages/golist_overlay.go
generated
vendored
@@ -102,8 +102,11 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||
}
|
||||
}
|
||||
}
|
||||
// The overlay could have included an entirely new package.
|
||||
if pkg == nil {
|
||||
// The overlay could have included an entirely new package or an
|
||||
// ad-hoc package. An ad-hoc package is one that we have manually
|
||||
// constructed from inadequate `go list` results for a file= query.
|
||||
// It will have the ID command-line-arguments.
|
||||
if pkg == nil || pkg.ID == "command-line-arguments" {
|
||||
// Try to find the module or gopath dir the file is contained in.
|
||||
// Then for modules, add the module opath to the beginning.
|
||||
pkgPath, ok, err := state.getPkgPath(dir)
|
||||
@@ -127,35 +130,40 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
|
||||
}
|
||||
}
|
||||
// Try to reclaim a package with the same ID, if it exists in the response.
|
||||
for _, p := range response.dr.Packages {
|
||||
if reclaimPackage(p, id, opath, contents) {
|
||||
pkg = p
|
||||
break
|
||||
}
|
||||
}
|
||||
// Otherwise, create a new package.
|
||||
if pkg == nil {
|
||||
pkg = &Package{
|
||||
PkgPath: pkgPath,
|
||||
ID: id,
|
||||
Name: pkgName,
|
||||
Imports: make(map[string]*Package),
|
||||
}
|
||||
response.addPackage(pkg)
|
||||
havePkgs[pkg.PkgPath] = id
|
||||
// Add the production package's sources for a test variant.
|
||||
if isTestFile && !isXTest && testVariantOf != nil {
|
||||
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
|
||||
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
|
||||
// Add the package under test and its imports to the test variant.
|
||||
pkg.forTest = testVariantOf.PkgPath
|
||||
for k, v := range testVariantOf.Imports {
|
||||
pkg.Imports[k] = &Package{ID: v.ID}
|
||||
if pkg != nil {
|
||||
// TODO(rstambler): We should change the package's path and ID
|
||||
// here. The only issue is that this messes with the roots.
|
||||
} else {
|
||||
// Try to reclaim a package with the same ID, if it exists in the response.
|
||||
for _, p := range response.dr.Packages {
|
||||
if reclaimPackage(p, id, opath, contents) {
|
||||
pkg = p
|
||||
break
|
||||
}
|
||||
}
|
||||
if isXTest {
|
||||
pkg.forTest = forTest
|
||||
// Otherwise, create a new package.
|
||||
if pkg == nil {
|
||||
pkg = &Package{
|
||||
PkgPath: pkgPath,
|
||||
ID: id,
|
||||
Name: pkgName,
|
||||
Imports: make(map[string]*Package),
|
||||
}
|
||||
response.addPackage(pkg)
|
||||
havePkgs[pkg.PkgPath] = id
|
||||
// Add the production package's sources for a test variant.
|
||||
if isTestFile && !isXTest && testVariantOf != nil {
|
||||
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
|
||||
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
|
||||
// Add the package under test and its imports to the test variant.
|
||||
pkg.forTest = testVariantOf.PkgPath
|
||||
for k, v := range testVariantOf.Imports {
|
||||
pkg.Imports[k] = &Package{ID: v.ID}
|
||||
}
|
||||
}
|
||||
if isXTest {
|
||||
pkg.forTest = forTest
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
vendor/golang.org/x/tools/imports/forward.go
generated
vendored
27
vendor/golang.org/x/tools/imports/forward.go
generated
vendored
@@ -3,8 +3,10 @@
|
||||
package imports // import "golang.org/x/tools/imports"
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
intimp "golang.org/x/tools/internal/imports"
|
||||
)
|
||||
|
||||
@@ -29,25 +31,34 @@ var Debug = false
|
||||
var LocalPrefix string
|
||||
|
||||
// Process formats and adjusts imports for the provided file.
|
||||
// If opt is nil the defaults are used.
|
||||
// If opt is nil the defaults are used, and if src is nil the source
|
||||
// is read from the filesystem.
|
||||
//
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
|
||||
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
||||
var err error
|
||||
if src == nil {
|
||||
src, err = ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if opt == nil {
|
||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||
}
|
||||
intopt := &intimp.Options{
|
||||
Env: &intimp.ProcessEnv{
|
||||
LocalPrefix: LocalPrefix,
|
||||
GocmdRunner: &gocommand.Runner{},
|
||||
},
|
||||
AllErrors: opt.AllErrors,
|
||||
Comments: opt.Comments,
|
||||
FormatOnly: opt.FormatOnly,
|
||||
Fragment: opt.Fragment,
|
||||
TabIndent: opt.TabIndent,
|
||||
TabWidth: opt.TabWidth,
|
||||
LocalPrefix: LocalPrefix,
|
||||
AllErrors: opt.AllErrors,
|
||||
Comments: opt.Comments,
|
||||
FormatOnly: opt.FormatOnly,
|
||||
Fragment: opt.Fragment,
|
||||
TabIndent: opt.TabIndent,
|
||||
TabWidth: opt.TabWidth,
|
||||
}
|
||||
if Debug {
|
||||
intopt.Env.Logf = log.Printf
|
||||
|
||||
73
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
73
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
@@ -32,25 +32,25 @@ import (
|
||||
|
||||
// importToGroup is a list of functions which map from an import path to
|
||||
// a group number.
|
||||
var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool){
|
||||
func(env *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
if env.LocalPrefix == "" {
|
||||
var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
|
||||
func(localPrefix, importPath string) (num int, ok bool) {
|
||||
if localPrefix == "" {
|
||||
return
|
||||
}
|
||||
for _, p := range strings.Split(env.LocalPrefix, ",") {
|
||||
for _, p := range strings.Split(localPrefix, ",") {
|
||||
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
|
||||
return 3, true
|
||||
}
|
||||
}
|
||||
return
|
||||
},
|
||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
func(_, importPath string) (num int, ok bool) {
|
||||
if strings.HasPrefix(importPath, "appengine") {
|
||||
return 2, true
|
||||
}
|
||||
return
|
||||
},
|
||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
func(_, importPath string) (num int, ok bool) {
|
||||
firstComponent := strings.Split(importPath, "/")[0]
|
||||
if strings.Contains(firstComponent, ".") {
|
||||
return 1, true
|
||||
@@ -59,9 +59,9 @@ var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool
|
||||
},
|
||||
}
|
||||
|
||||
func importGroup(env *ProcessEnv, importPath string) int {
|
||||
func importGroup(localPrefix, importPath string) int {
|
||||
for _, fn := range importToGroup {
|
||||
if n, ok := fn(env, importPath); ok {
|
||||
if n, ok := fn(localPrefix, importPath); ok {
|
||||
return n
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,12 @@ func (p *pass) loadPackageNames(imports []*ImportInfo) error {
|
||||
unknown = append(unknown, imp.ImportPath)
|
||||
}
|
||||
|
||||
names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir)
|
||||
resolver, err := p.env.GetResolver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names, err := resolver.loadPackageNames(unknown, p.srcDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -640,15 +645,23 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena
|
||||
wrappedCallback.exportsLoaded(pkg, exports)
|
||||
},
|
||||
}
|
||||
return env.GetResolver().scan(ctx, scanFilter)
|
||||
resolver, err := env.GetResolver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return resolver.scan(ctx, scanFilter)
|
||||
}
|
||||
|
||||
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) map[string]int {
|
||||
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]int, error) {
|
||||
result := make(map[string]int)
|
||||
for _, path := range paths {
|
||||
result[path] = env.GetResolver().scoreImportPath(ctx, path)
|
||||
resolver, err := env.GetResolver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result
|
||||
for _, path := range paths {
|
||||
result[path] = resolver.scoreImportPath(ctx, path)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func PrimeCache(ctx context.Context, env *ProcessEnv) error {
|
||||
@@ -674,8 +687,9 @@ func candidateImportName(pkg *pkg) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// getAllCandidates gets all of the candidates to be imported, regardless of if they are needed.
|
||||
func getAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
||||
// GetAllCandidates gets all of the packages starting with prefix that can be
|
||||
// imported by filename, sorted by import path.
|
||||
func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
||||
callback := &scanCallback{
|
||||
rootFound: func(gopathwalk.Root) bool {
|
||||
return true
|
||||
@@ -714,7 +728,8 @@ type PackageExport struct {
|
||||
Exports []string
|
||||
}
|
||||
|
||||
func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
|
||||
// GetPackageExports returns all known packages with name pkg and their exports.
|
||||
func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
|
||||
callback := &scanCallback{
|
||||
rootFound: func(gopathwalk.Root) bool {
|
||||
return true
|
||||
@@ -749,8 +764,6 @@ var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD"
|
||||
// ProcessEnv contains environment variables and settings that affect the use of
|
||||
// the go command, the go/build package, etc.
|
||||
type ProcessEnv struct {
|
||||
LocalPrefix string
|
||||
|
||||
GocmdRunner *gocommand.Runner
|
||||
|
||||
BuildFlags []string
|
||||
@@ -830,16 +843,19 @@ func (e *ProcessEnv) env() []string {
|
||||
return env
|
||||
}
|
||||
|
||||
func (e *ProcessEnv) GetResolver() Resolver {
|
||||
func (e *ProcessEnv) GetResolver() (Resolver, error) {
|
||||
if e.resolver != nil {
|
||||
return e.resolver
|
||||
return e.resolver, nil
|
||||
}
|
||||
if err := e.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(e.Env["GOMOD"]) == 0 {
|
||||
e.resolver = newGopathResolver(e)
|
||||
return e.resolver
|
||||
return e.resolver, nil
|
||||
}
|
||||
e.resolver = newModuleResolver(e)
|
||||
return e.resolver
|
||||
return e.resolver, nil
|
||||
}
|
||||
|
||||
func (e *ProcessEnv) buildContext() *build.Context {
|
||||
@@ -964,10 +980,13 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||
return false // We'll do our own loading after we sort.
|
||||
},
|
||||
}
|
||||
err := pass.env.GetResolver().scan(context.Background(), callback)
|
||||
resolver, err := pass.env.GetResolver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resolver.scan(context.Background(), callback); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Search for imports matching potential package references.
|
||||
type result struct {
|
||||
@@ -1408,6 +1427,10 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
||||
pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
|
||||
}
|
||||
}
|
||||
resolver, err := pass.env.GetResolver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collect exports for packages with matching names.
|
||||
rescv := make([]chan *pkg, len(candidates))
|
||||
@@ -1446,7 +1469,7 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
||||
}
|
||||
// If we're an x_test, load the package under test's test variant.
|
||||
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
|
||||
_, exports, err := pass.env.GetResolver().loadExports(ctx, c.pkg, includeTest)
|
||||
_, exports, err := resolver.loadExports(ctx, c.pkg, includeTest)
|
||||
if err != nil {
|
||||
if pass.env.Logf != nil {
|
||||
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
||||
|
||||
88
vendor/golang.org/x/tools/internal/imports/imports.go
generated
vendored
88
vendor/golang.org/x/tools/internal/imports/imports.go
generated
vendored
@@ -11,7 +11,6 @@ package imports
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
@@ -19,19 +18,22 @@ import (
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
)
|
||||
|
||||
// Options is golang.org/x/tools/imports.Options with extra internal-only options.
|
||||
type Options struct {
|
||||
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
|
||||
|
||||
// LocalPrefix is a comma-separated string of import path prefixes, which, if
|
||||
// set, instructs Process to sort the import paths with the given prefixes
|
||||
// into another group after 3rd-party packages.
|
||||
LocalPrefix string
|
||||
|
||||
Fragment bool // Accept fragment of a source file (no package statement)
|
||||
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
||||
|
||||
@@ -42,13 +44,8 @@ type Options struct {
|
||||
FormatOnly bool // Disable the insertion and deletion of imports
|
||||
}
|
||||
|
||||
// Process implements golang.org/x/tools/imports.Process with explicit context in env.
|
||||
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
|
||||
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
|
||||
src, opt, err = initialize(filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
file, adjust, err := parse(fileSet, filename, src, opt)
|
||||
if err != nil {
|
||||
@@ -64,16 +61,12 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
|
||||
}
|
||||
|
||||
// FixImports returns a list of fixes to the imports that, when applied,
|
||||
// will leave the imports in the same state as Process.
|
||||
// will leave the imports in the same state as Process. src and opt must
|
||||
// be specified.
|
||||
//
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
|
||||
src, opt, err = initialize(filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
file, _, err := parse(fileSet, filename, src, opt)
|
||||
if err != nil {
|
||||
@@ -84,13 +77,9 @@ func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix,
|
||||
}
|
||||
|
||||
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
|
||||
// is added in when parsing the file.
|
||||
// is added in when parsing the file. src and opts must be specified, but no
|
||||
// env is needed.
|
||||
func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) {
|
||||
src, opt, err = initialize(filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Don't use parse() -- we don't care about fragments or statement lists
|
||||
// here, and we need to work with unparseable files.
|
||||
fileSet := token.NewFileSet()
|
||||
@@ -114,60 +103,9 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
|
||||
return formatFile(fileSet, file, src, nil, opt)
|
||||
}
|
||||
|
||||
// GetAllCandidates gets all of the packages starting with prefix that can be
|
||||
// imported by filename, sorted by import path.
|
||||
func GetAllCandidates(ctx context.Context, callback func(ImportFix), searchPrefix, filename, filePkg string, opt *Options) error {
|
||||
_, opt, err := initialize(filename, []byte{}, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return getAllCandidates(ctx, callback, searchPrefix, filename, filePkg, opt.Env)
|
||||
}
|
||||
|
||||
// GetPackageExports returns all known packages with name pkg and their exports.
|
||||
func GetPackageExports(ctx context.Context, callback func(PackageExport), searchPkg, filename, filePkg string, opt *Options) error {
|
||||
_, opt, err := initialize(filename, []byte{}, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return getPackageExports(ctx, callback, searchPkg, filename, filePkg, opt.Env)
|
||||
}
|
||||
|
||||
// initialize sets the values for opt and src.
|
||||
// If they are provided, they are not changed. Otherwise opt is set to the
|
||||
// default values and src is read from the file system.
|
||||
func initialize(filename string, src []byte, opt *Options) ([]byte, *Options, error) {
|
||||
// Use defaults if opt is nil.
|
||||
if opt == nil {
|
||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||
}
|
||||
|
||||
// Set the env if the user has not provided it.
|
||||
if opt.Env == nil {
|
||||
opt.Env = &ProcessEnv{}
|
||||
}
|
||||
// Set the gocmdRunner if the user has not provided it.
|
||||
if opt.Env.GocmdRunner == nil {
|
||||
opt.Env.GocmdRunner = &gocommand.Runner{}
|
||||
}
|
||||
if err := opt.Env.init(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if src == nil {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
src = b
|
||||
}
|
||||
|
||||
return src, opt, nil
|
||||
}
|
||||
|
||||
func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
|
||||
mergeImports(opt.Env, fileSet, file)
|
||||
sortImports(opt.Env, fileSet, file)
|
||||
mergeImports(fileSet, file)
|
||||
sortImports(opt.LocalPrefix, fileSet, file)
|
||||
imps := astutil.Imports(fileSet, file)
|
||||
var spacesBefore []string // import paths we need spaces before
|
||||
for _, impSection := range imps {
|
||||
@@ -178,7 +116,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
|
||||
lastGroup := -1
|
||||
for _, importSpec := range impSection {
|
||||
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
||||
groupNum := importGroup(opt.Env, importPath)
|
||||
groupNum := importGroup(opt.LocalPrefix, importPath)
|
||||
if groupNum != lastGroup && lastGroup != -1 {
|
||||
spacesBefore = append(spacesBefore, importPath)
|
||||
}
|
||||
|
||||
20
vendor/golang.org/x/tools/internal/imports/sortimports.go
generated
vendored
20
vendor/golang.org/x/tools/internal/imports/sortimports.go
generated
vendored
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// sortImports sorts runs of consecutive import lines in import blocks in f.
|
||||
// It also removes duplicate imports when it is possible to do so without data loss.
|
||||
func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||
func sortImports(localPrefix string, fset *token.FileSet, f *ast.File) {
|
||||
for i, d := range f.Decls {
|
||||
d, ok := d.(*ast.GenDecl)
|
||||
if !ok || d.Tok != token.IMPORT {
|
||||
@@ -40,11 +40,11 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||
for j, s := range d.Specs {
|
||||
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
||||
// j begins a new run. End this one.
|
||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:j])...)
|
||||
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:j])...)
|
||||
i = j
|
||||
}
|
||||
}
|
||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:])...)
|
||||
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:])...)
|
||||
d.Specs = specs
|
||||
|
||||
// Deduping can leave a blank line before the rparen; clean that up.
|
||||
@@ -60,7 +60,7 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||
|
||||
// mergeImports merges all the import declarations into the first one.
|
||||
// Taken from golang.org/x/tools/ast/astutil.
|
||||
func mergeImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||
func mergeImports(fset *token.FileSet, f *ast.File) {
|
||||
if len(f.Decls) <= 1 {
|
||||
return
|
||||
}
|
||||
@@ -142,7 +142,7 @@ type posSpan struct {
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
func sortSpecs(localPrefix string, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
// Can't short-circuit here even if specs are already sorted,
|
||||
// since they might yet need deduplication.
|
||||
// A lone import, however, may be safely ignored.
|
||||
@@ -191,7 +191,7 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
|
||||
// Reassign the import paths to have the same position sequence.
|
||||
// Reassign each comment to abut the end of its spec.
|
||||
// Sort the comments by new position.
|
||||
sort.Sort(byImportSpec{env, specs})
|
||||
sort.Sort(byImportSpec{localPrefix, specs})
|
||||
|
||||
// Dedup. Thanks to our sorting, we can just consider
|
||||
// adjacent pairs of imports.
|
||||
@@ -245,8 +245,8 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
|
||||
}
|
||||
|
||||
type byImportSpec struct {
|
||||
env *ProcessEnv
|
||||
specs []ast.Spec // slice of *ast.ImportSpec
|
||||
localPrefix string
|
||||
specs []ast.Spec // slice of *ast.ImportSpec
|
||||
}
|
||||
|
||||
func (x byImportSpec) Len() int { return len(x.specs) }
|
||||
@@ -255,8 +255,8 @@ func (x byImportSpec) Less(i, j int) bool {
|
||||
ipath := importPath(x.specs[i])
|
||||
jpath := importPath(x.specs[j])
|
||||
|
||||
igroup := importGroup(x.env, ipath)
|
||||
jgroup := importGroup(x.env, jpath)
|
||||
igroup := importGroup(x.localPrefix, ipath)
|
||||
jgroup := importGroup(x.localPrefix, jpath)
|
||||
if igroup != jgroup {
|
||||
return igroup < jgroup
|
||||
}
|
||||
|
||||
52
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
52
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
@@ -56,6 +56,7 @@ var stdlib = map[string][]string{
|
||||
},
|
||||
"bufio": []string{
|
||||
"ErrAdvanceTooFar",
|
||||
"ErrBadReadCount",
|
||||
"ErrBufferFull",
|
||||
"ErrFinalToken",
|
||||
"ErrInvalidUnreadByte",
|
||||
@@ -303,7 +304,9 @@ var stdlib = map[string][]string{
|
||||
"PrivateKey",
|
||||
"PublicKey",
|
||||
"Sign",
|
||||
"SignASN1",
|
||||
"Verify",
|
||||
"VerifyASN1",
|
||||
},
|
||||
"crypto/ed25519": []string{
|
||||
"GenerateKey",
|
||||
@@ -322,11 +325,13 @@ var stdlib = map[string][]string{
|
||||
"CurveParams",
|
||||
"GenerateKey",
|
||||
"Marshal",
|
||||
"MarshalCompressed",
|
||||
"P224",
|
||||
"P256",
|
||||
"P384",
|
||||
"P521",
|
||||
"Unmarshal",
|
||||
"UnmarshalCompressed",
|
||||
},
|
||||
"crypto/hmac": []string{
|
||||
"Equal",
|
||||
@@ -432,6 +437,7 @@ var stdlib = map[string][]string{
|
||||
"CurveP521",
|
||||
"Dial",
|
||||
"DialWithDialer",
|
||||
"Dialer",
|
||||
"ECDSAWithP256AndSHA256",
|
||||
"ECDSAWithP384AndSHA384",
|
||||
"ECDSAWithP521AndSHA512",
|
||||
@@ -507,6 +513,7 @@ var stdlib = map[string][]string{
|
||||
"ConstraintViolationError",
|
||||
"CreateCertificate",
|
||||
"CreateCertificateRequest",
|
||||
"CreateRevocationList",
|
||||
"DSA",
|
||||
"DSAWithSHA1",
|
||||
"DSAWithSHA256",
|
||||
@@ -581,6 +588,7 @@ var stdlib = map[string][]string{
|
||||
"PublicKeyAlgorithm",
|
||||
"PureEd25519",
|
||||
"RSA",
|
||||
"RevocationList",
|
||||
"SHA1WithRSA",
|
||||
"SHA256WithRSA",
|
||||
"SHA256WithRSAPSS",
|
||||
@@ -694,6 +702,7 @@ var stdlib = map[string][]string{
|
||||
"String",
|
||||
"Tx",
|
||||
"TxOptions",
|
||||
"Validator",
|
||||
"Value",
|
||||
"ValueConverter",
|
||||
"Valuer",
|
||||
@@ -2349,6 +2358,27 @@ var stdlib = map[string][]string{
|
||||
"IMAGE_DIRECTORY_ENTRY_RESOURCE",
|
||||
"IMAGE_DIRECTORY_ENTRY_SECURITY",
|
||||
"IMAGE_DIRECTORY_ENTRY_TLS",
|
||||
"IMAGE_DLLCHARACTERISTICS_APPCONTAINER",
|
||||
"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE",
|
||||
"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY",
|
||||
"IMAGE_DLLCHARACTERISTICS_GUARD_CF",
|
||||
"IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA",
|
||||
"IMAGE_DLLCHARACTERISTICS_NO_BIND",
|
||||
"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION",
|
||||
"IMAGE_DLLCHARACTERISTICS_NO_SEH",
|
||||
"IMAGE_DLLCHARACTERISTICS_NX_COMPAT",
|
||||
"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE",
|
||||
"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER",
|
||||
"IMAGE_FILE_32BIT_MACHINE",
|
||||
"IMAGE_FILE_AGGRESIVE_WS_TRIM",
|
||||
"IMAGE_FILE_BYTES_REVERSED_HI",
|
||||
"IMAGE_FILE_BYTES_REVERSED_LO",
|
||||
"IMAGE_FILE_DEBUG_STRIPPED",
|
||||
"IMAGE_FILE_DLL",
|
||||
"IMAGE_FILE_EXECUTABLE_IMAGE",
|
||||
"IMAGE_FILE_LARGE_ADDRESS_AWARE",
|
||||
"IMAGE_FILE_LINE_NUMS_STRIPPED",
|
||||
"IMAGE_FILE_LOCAL_SYMS_STRIPPED",
|
||||
"IMAGE_FILE_MACHINE_AM33",
|
||||
"IMAGE_FILE_MACHINE_AMD64",
|
||||
"IMAGE_FILE_MACHINE_ARM",
|
||||
@@ -2371,6 +2401,25 @@ var stdlib = map[string][]string{
|
||||
"IMAGE_FILE_MACHINE_THUMB",
|
||||
"IMAGE_FILE_MACHINE_UNKNOWN",
|
||||
"IMAGE_FILE_MACHINE_WCEMIPSV2",
|
||||
"IMAGE_FILE_NET_RUN_FROM_SWAP",
|
||||
"IMAGE_FILE_RELOCS_STRIPPED",
|
||||
"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP",
|
||||
"IMAGE_FILE_SYSTEM",
|
||||
"IMAGE_FILE_UP_SYSTEM_ONLY",
|
||||
"IMAGE_SUBSYSTEM_EFI_APPLICATION",
|
||||
"IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
|
||||
"IMAGE_SUBSYSTEM_EFI_ROM",
|
||||
"IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
|
||||
"IMAGE_SUBSYSTEM_NATIVE",
|
||||
"IMAGE_SUBSYSTEM_NATIVE_WINDOWS",
|
||||
"IMAGE_SUBSYSTEM_OS2_CUI",
|
||||
"IMAGE_SUBSYSTEM_POSIX_CUI",
|
||||
"IMAGE_SUBSYSTEM_UNKNOWN",
|
||||
"IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
|
||||
"IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
|
||||
"IMAGE_SUBSYSTEM_WINDOWS_CUI",
|
||||
"IMAGE_SUBSYSTEM_WINDOWS_GUI",
|
||||
"IMAGE_SUBSYSTEM_XBOX",
|
||||
"ImportDirectory",
|
||||
"NewFile",
|
||||
"Open",
|
||||
@@ -4188,6 +4237,7 @@ var stdlib = map[string][]string{
|
||||
"DevNull",
|
||||
"Environ",
|
||||
"ErrClosed",
|
||||
"ErrDeadlineExceeded",
|
||||
"ErrExist",
|
||||
"ErrInvalid",
|
||||
"ErrNoDeadline",
|
||||
@@ -4646,6 +4696,7 @@ var stdlib = map[string][]string{
|
||||
"ErrRange",
|
||||
"ErrSyntax",
|
||||
"FormatBool",
|
||||
"FormatComplex",
|
||||
"FormatFloat",
|
||||
"FormatInt",
|
||||
"FormatUint",
|
||||
@@ -4655,6 +4706,7 @@ var stdlib = map[string][]string{
|
||||
"Itoa",
|
||||
"NumError",
|
||||
"ParseBool",
|
||||
"ParseComplex",
|
||||
"ParseFloat",
|
||||
"ParseInt",
|
||||
"ParseUint",
|
||||
|
||||
12
vendor/modules.txt
vendored
12
vendor/modules.txt
vendored
@@ -515,7 +515,12 @@ github.com/willf/bitset
|
||||
# github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c
|
||||
## explicit
|
||||
github.com/xtgo/uuid
|
||||
# go.etcd.io/bbolt v1.3.4
|
||||
# github.com/ziutek/mymysql v1.5.4
|
||||
## explicit
|
||||
# github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a
|
||||
## explicit
|
||||
# go.etcd.io/bbolt v1.3.5
|
||||
## explicit
|
||||
go.etcd.io/bbolt
|
||||
# go.uber.org/atomic v1.6.0
|
||||
go.uber.org/atomic
|
||||
@@ -612,7 +617,7 @@ golang.org/x/text/secure/bidirule
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/bidi
|
||||
golang.org/x/text/unicode/norm
|
||||
# golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f
|
||||
# golang.org/x/tools v0.0.0-20200806022845-90696ccdc692
|
||||
## explicit
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
golang.org/x/tools/go/gcexportdata
|
||||
@@ -630,7 +635,8 @@ golang.org/x/tools/internal/gopathwalk
|
||||
golang.org/x/tools/internal/imports
|
||||
golang.org/x/tools/internal/packagesinternal
|
||||
golang.org/x/tools/internal/typesinternal
|
||||
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
## explicit
|
||||
golang.org/x/xerrors
|
||||
golang.org/x/xerrors/internal
|
||||
# google.golang.org/appengine v1.6.6
|
||||
|
||||
Reference in New Issue
Block a user