From 136c6286a8ee140ed1dca3b5bc80e25351b3b870 Mon Sep 17 00:00:00 2001 From: Sven Seeberg Date: Sun, 30 May 2021 13:43:51 +0200 Subject: [PATCH 01/19] Add LDAP group sync to Teams, fixes #1395 * Add setting for a JSON that maps LDAP groups to Org Teams. * Add log trace when removing or adding team members. * Sync is being run on login and periodically. * Existing group filter settings are reused. Co-authored-by: Giuliano Mele Co-authored-by: Sven Seeberg --- cmd/admin_auth_ldap.go | 14 + go.mod | 1 + go.sum | 2 + models/login_source.go | 86 +- models/user.go | 4 + modules/auth/ldap/README.md | 8 + modules/auth/ldap/ldap.go | 137 ++- options/locale/locale_en-US.ini | 3 + routers/web/admin/auths.go | 3 + services/forms/auth_form.go | 3 + templates/admin/auth/edit.tmpl | 26 +- templates/admin/auth/source/ldap.tmpl | 26 +- vendor/github.com/thoas/go-funk/.gitignore | 27 + vendor/github.com/thoas/go-funk/.travis.yml | 7 + vendor/github.com/thoas/go-funk/CHANGELOG.md | 29 + vendor/github.com/thoas/go-funk/LICENSE | 21 + vendor/github.com/thoas/go-funk/Makefile | 5 + vendor/github.com/thoas/go-funk/README.rst | 835 +++++++++++++++ vendor/github.com/thoas/go-funk/assign.go | 129 +++ vendor/github.com/thoas/go-funk/builder.go | 110 ++ .../github.com/thoas/go-funk/chain_builder.go | 142 +++ vendor/github.com/thoas/go-funk/compact.go | 50 + vendor/github.com/thoas/go-funk/fill.go | 34 + vendor/github.com/thoas/go-funk/go.mod | 5 + vendor/github.com/thoas/go-funk/go.sum | 11 + vendor/github.com/thoas/go-funk/helpers.go | 317 ++++++ .../github.com/thoas/go-funk/intersection.go | 253 +++++ vendor/github.com/thoas/go-funk/join.go | 86 ++ .../thoas/go-funk/join_primitives.go | 385 +++++++ .../github.com/thoas/go-funk/lazy_builder.go | 117 +++ vendor/github.com/thoas/go-funk/map.go | 74 ++ vendor/github.com/thoas/go-funk/max.go | 194 ++++ vendor/github.com/thoas/go-funk/min.go | 193 ++++ vendor/github.com/thoas/go-funk/operation.go | 69 ++ .../github.com/thoas/go-funk/permutation.go | 29 + vendor/github.com/thoas/go-funk/predicate.go | 47 + vendor/github.com/thoas/go-funk/presence.go | 207 ++++ vendor/github.com/thoas/go-funk/reduce.go | 87 ++ vendor/github.com/thoas/go-funk/retrieve.go | 93 ++ vendor/github.com/thoas/go-funk/scan.go | 192 ++++ vendor/github.com/thoas/go-funk/short_if.go | 8 + vendor/github.com/thoas/go-funk/subset.go | 41 + .../github.com/thoas/go-funk/subtraction.go | 87 ++ vendor/github.com/thoas/go-funk/transform.go | 555 ++++++++++ vendor/github.com/thoas/go-funk/typesafe.go | 967 ++++++++++++++++++ vendor/github.com/thoas/go-funk/utils.go | 103 ++ vendor/github.com/thoas/go-funk/without.go | 19 + vendor/github.com/thoas/go-funk/zip.go | 46 + vendor/modules.txt | 3 + web_src/js/index.js | 22 +- 50 files changed, 5874 insertions(+), 38 deletions(-) create mode 100644 vendor/github.com/thoas/go-funk/.gitignore create mode 100644 vendor/github.com/thoas/go-funk/.travis.yml create mode 100644 vendor/github.com/thoas/go-funk/CHANGELOG.md create mode 100644 vendor/github.com/thoas/go-funk/LICENSE create mode 100644 vendor/github.com/thoas/go-funk/Makefile create mode 100644 vendor/github.com/thoas/go-funk/README.rst create mode 100644 vendor/github.com/thoas/go-funk/assign.go create mode 100644 vendor/github.com/thoas/go-funk/builder.go create mode 100644 vendor/github.com/thoas/go-funk/chain_builder.go create mode 100644 vendor/github.com/thoas/go-funk/compact.go create mode 100644 vendor/github.com/thoas/go-funk/fill.go create mode 100644 vendor/github.com/thoas/go-funk/go.mod create mode 100644 vendor/github.com/thoas/go-funk/go.sum create mode 100644 vendor/github.com/thoas/go-funk/helpers.go create mode 100644 vendor/github.com/thoas/go-funk/intersection.go create mode 100644 vendor/github.com/thoas/go-funk/join.go create mode 100644 vendor/github.com/thoas/go-funk/join_primitives.go create mode 100644 vendor/github.com/thoas/go-funk/lazy_builder.go create mode 100644 vendor/github.com/thoas/go-funk/map.go create mode 100644 vendor/github.com/thoas/go-funk/max.go create mode 100644 vendor/github.com/thoas/go-funk/min.go create mode 100644 vendor/github.com/thoas/go-funk/operation.go create mode 100644 vendor/github.com/thoas/go-funk/permutation.go create mode 100644 vendor/github.com/thoas/go-funk/predicate.go create mode 100644 vendor/github.com/thoas/go-funk/presence.go create mode 100644 vendor/github.com/thoas/go-funk/reduce.go create mode 100644 vendor/github.com/thoas/go-funk/retrieve.go create mode 100644 vendor/github.com/thoas/go-funk/scan.go create mode 100644 vendor/github.com/thoas/go-funk/short_if.go create mode 100644 vendor/github.com/thoas/go-funk/subset.go create mode 100644 vendor/github.com/thoas/go-funk/subtraction.go create mode 100644 vendor/github.com/thoas/go-funk/transform.go create mode 100644 vendor/github.com/thoas/go-funk/typesafe.go create mode 100644 vendor/github.com/thoas/go-funk/utils.go create mode 100644 vendor/github.com/thoas/go-funk/without.go create mode 100644 vendor/github.com/thoas/go-funk/zip.go diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 5ab64ec7d53c5..7fc54b24a31e4 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -89,6 +89,14 @@ var ( Name: "public-ssh-key-attribute", Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.", }, + cli.StringFlag{ + Name: "team-group-map", + Usage: "Map of LDAP groups to teams.", + }, + cli.StringFlag{ + Name: "team-group-map-force", + Usage: "Force synchronization of mapped LDAP groups to teams.", + }, } ldapBindDnCLIFlags = append(commonLdapCLIFlags, @@ -245,6 +253,12 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error { if c.IsSet("allow-deactivate-all") { config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all") } + if c.IsSet("team-group-map") { + config.Source.TeamGroupMap = c.String("team-group-map") + } + if c.IsSet("team-group-map-removal") { + config.Source.TeamGroupMapRemoval = c.Bool("team-group-map-removal") + } return nil } diff --git a/go.mod b/go.mod index 5032acce990fe..2bb6f9b7ba52e 100644 --- a/go.mod +++ b/go.mod @@ -106,6 +106,7 @@ require ( github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 + github.com/thoas/go-funk v0.8.0 github.com/tstranex/u2f v1.0.0 github.com/ulikunitz/xz v0.5.10 // indirect github.com/unknwon/com v1.0.1 diff --git a/go.sum b/go.sum index fc6e2422c8496..077560747b4e5 100644 --- a/go.sum +++ b/go.sum @@ -1018,6 +1018,8 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/thoas/go-funk v0.8.0 h1:JP9tKSvnpFVclYgDM0Is7FD9M4fhPvqA0s0BsXmzSRQ= +github.com/thoas/go-funk v0.8.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= diff --git a/models/login_source.go b/models/login_source.go index f9bd496b3add4..90bc4ca313550 100644 --- a/models/login_source.go +++ b/models/login_source.go @@ -491,6 +491,46 @@ func composeFullName(firstname, surname, username string) string { } } +// remove membership to organizations/teams if user is not member of corresponding LDAP group +// e.g. lets assume user is member of LDAP group "x", but LDAP group team map contains LDAP groups "x" and "y" +// then users membership gets removed for all organizations/teams mapped by LDAP group "y" +func removeMappedMemberships(user *User, ldapTeamRemove map[string][]string) { + for orgName, teamNames := range ldapTeamRemove { + org, err := GetOrgByName(orgName) + if err != nil { + // organization must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) + continue + } + for _, teamName := range teamNames { + team, err := org.GetTeam(teamName) + if err != nil { + // team must must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + continue + } + if isMember, err := IsTeamMember(org.ID, team.ID, user.ID); isMember && err == nil { + log.Trace("LDAP group sync: removing user [%s] from team [%s]", user.Name, org.Name) + } + err = team.RemoveMember(user.ID) + if err != nil { + log.Error("LDAP group sync: Could not remove user from team: %v", err) + } + } + if remainingTeams, err := GetUserOrgTeams(org.ID, user.ID); err == nil && len(remainingTeams) == 0 { + if isMember, err := IsOrganizationMember(org.ID, user.ID); isMember && err == nil { + log.Trace("LDAP group sync: removing user [%s] from organization [%s]", user.Name, org.Name) + } + err = org.RemoveMember(user.ID) + if err != nil { + log.Error("LDAP group sync: Could not remove user from organization: %v", err) + } + } else if err != nil { + log.Error("LDAP group sync: Could not find users [id: %d] teams for given organization [%s]", user.ID, org.Name) + } + } +} + // LoginViaLDAP queries if login/password is valid against the LDAP directory pool, // and create a local user if success when enabled. func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*User, error) { @@ -537,7 +577,9 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(user, source, sr.SSHPublicKey) { return user, RewriteAllPublicKeys() } - + if source.LDAP().TeamGroupMapEnabled || source.LDAP().TeamGroupMapRemoval { + SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, source) + } return user, nil } @@ -568,10 +610,50 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use if err == nil && isAttributeSSHPublicKeySet && addLdapSSHPublicKeys(user, source, sr.SSHPublicKey) { err = RewriteAllPublicKeys() } - + if source.LDAP().TeamGroupMapEnabled || source.LDAP().TeamGroupMapRemoval { + SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, source) + } return user, err } +// SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships +func SyncLdapGroupsToTeams(user *User, ldapTeamAdd map[string][]string, ldapTeamRemove map[string][]string, source *LoginSource) { + if source.LDAP().TeamGroupMapRemoval { + // when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships + removeMappedMemberships(user, ldapTeamRemove) + } + for orgName, teamNames := range ldapTeamAdd { + org, err := GetOrgByName(orgName) + if err != nil { + // organization must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) + continue + } + if isMember, err := IsOrganizationMember(org.ID, user.ID); !isMember && err == nil { + log.Trace("LDAP group sync: adding user [%s] to organization [%s]", user.Name, org.Name) + } + err = org.AddMember(user.ID) + if err != nil { + log.Error("LDAP group sync: Could not add user to organization: %v", err) + } + for _, teamName := range teamNames { + team, err := org.GetTeam(teamName) + if err != nil { + // team must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + continue + } + if isMember, err := IsTeamMember(org.ID, team.ID, user.ID); !isMember && err == nil { + log.Trace("LDAP group sync: adding user [%s] to team [%s]", user.Name, org.Name) + } + err = team.AddMember(user.ID) + if err != nil { + log.Error("LDAP group sync: Could not add user to team: %v", err) + } + } + } +} + // _________ __________________________ // / _____/ / \__ ___/\______ \ // \_____ \ / \ / \| | | ___/ diff --git a/models/user.go b/models/user.go index 3b8ce79251f0c..887dc40f3ce9d 100644 --- a/models/user.go +++ b/models/user.go @@ -2014,6 +2014,10 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error { } } } + // Synchronize LDAP groups with organization and team memberships + if s.LDAP().TeamGroupMapEnabled || s.LDAP().TeamGroupMapRemoval { + SyncLdapGroupsToTeams(usr, su.LdapTeamAdd, su.LdapTeamRemove, s) + } } // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed diff --git a/modules/auth/ldap/README.md b/modules/auth/ldap/README.md index 76841f44aefd4..3c65dafdfa785 100644 --- a/modules/auth/ldap/README.md +++ b/modules/auth/ldap/README.md @@ -121,3 +121,11 @@ share the following fields: * Group Attribute for User (optional) * Which group LDAP attribute contains an array above user attribute names. * Example: memberUid + +* Team group map (optional) + * Automatically add users to Organization teams, depending on LDAP group memberships. + * Note: this function only adds users to teams, it never removes users. + * Example: {"cn=MyGroup,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2", ...], ...}, ...} + +* Team group map removal (optional) + * If set to true, users will be removed from teams if they are not members of the corresponding group. diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 91ad33a60f3a4..92eceed025585 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -9,12 +9,14 @@ package ldap import ( "crypto/tls" + "encoding/json" "fmt" "strings" "code.gitea.io/gitea/modules/log" "github.com/go-ldap/ldap/v3" + "github.com/thoas/go-funk" ) // SecurityProtocol protocol type @@ -56,17 +58,22 @@ type Source struct { GroupFilter string // Group Name Filter GroupMemberUID string // Group Attribute containing array of UserUID UserUID string // User Attribute listed in Group + TeamGroupMap string // Map LDAP groups to teams + TeamGroupMapRemoval bool // Remove user from teams which are synchronized and user is not a member of the corresponding LDAP group + TeamGroupMapEnabled bool // if LDAP groups mapping to gitea organizations teams is enabled } // SearchResult : user data type SearchResult struct { - Username string // Username - Name string // Name - Surname string // Surname - Mail string // E-mail address - SSHPublicKey []string // SSH Public Key - IsAdmin bool // if user is administrator - IsRestricted bool // if user is restricted + Username string // Username + Name string // Name + Surname string // Surname + Mail string // E-mail address + SSHPublicKey []string // SSH Public Key + IsAdmin bool // if user is administrator + IsRestricted bool // if user is restricted + LdapTeamAdd map[string][]string // organizations teams to add + LdapTeamRemove map[string][]string // organizations teams to remove } func (ls *Source) sanitizedUserQuery(username string) (string, bool) { @@ -230,6 +237,74 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool { return false } +// List all group memberships of a user +func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { + var ldapGroups []string + var groupFilter = fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) + result, err := l.Search(ldap.NewSearchRequest( + ls.GroupDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + groupFilter, + []string{}, + nil, + )) + if err != nil { + log.Error("Failed group search using filter[%s]: %v", groupFilter, err) + return ldapGroups + } + + for _, entry := range result.Entries { + if entry.DN == "" { + log.Error("LDAP search was successful, but found no DN!") + continue + } + ldapGroups = append(ldapGroups, entry.DN) + } + + return ldapGroups +} + +// parse LDAP groups and return map of ldap groups to organizations teams +func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { + ldapGroupsToTeams := make(map[string]map[string][]string) + err := json.Unmarshal([]byte(ls.TeamGroupMap), &ldapGroupsToTeams) + if err != nil { + log.Debug("Failed to unmarshall LDAP teams map: %v", err) + return nil + } + return ldapGroupsToTeams +} + +func (ls *Source) getMappedTeams(l *ldap.Conn, uid string) (map[string][]string, map[string][]string) { + teamsToAdd := map[string][]string{} + teamsToRemove := map[string][]string{} + // get all LDAP group memberships for user + usersLdapGroups := ls.listLdapGroupMemberships(l, uid) + // unmarshall LDAP group team map from configs + ldapGroupsToTeams := ls.mapLdapGroupsToTeams() + // select all LDAP groups from settings + allLdapGroups := funk.Keys(ldapGroupsToTeams).([]string) + // contains LDAP config groups, which the user is a member of + usersLdapGroupsToAdd := funk.IntersectString(allLdapGroups, usersLdapGroups) + // contains LDAP config groups, which the user is not a member of + usersLdapGroupToRemove, _ := funk.DifferenceString(allLdapGroups, usersLdapGroups) + for _, groupToAdd := range usersLdapGroupsToAdd { + for k, v := range ldapGroupsToTeams[groupToAdd] { + teamsToAdd[k] = v + } + } + for _, groupToRemove := range usersLdapGroupToRemove { + for k, v := range ldapGroupsToTeams[groupToRemove] { + teamsToRemove[k] = v + } + } + return teamsToAdd, teamsToRemove +} + // SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult { // See https://tools.ietf.org/search/rfc4513#section-5.1.2 @@ -341,6 +416,9 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul surname := sr.Entries[0].GetAttributeValue(ls.AttributeSurname) mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail) uid := sr.Entries[0].GetAttributeValue(ls.UserUID) + if ls.UserUID == "dn" || ls.UserUID == "DN" { + uid = sr.Entries[0].DN + } // Check group membership if ls.GroupsEnabled { @@ -402,14 +480,22 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul } } + teamsToAdd := make(map[string][]string) + teamsToRemove := make(map[string][]string) + if ls.TeamGroupMapEnabled || ls.TeamGroupMapRemoval { + teamsToAdd, teamsToRemove = ls.getMappedTeams(l, uid) + } + return &SearchResult{ - Username: username, - Name: firstname, - Surname: surname, - Mail: mail, - SSHPublicKey: sshPublicKey, - IsAdmin: isAdmin, - IsRestricted: isRestricted, + Username: username, + Name: firstname, + Surname: surname, + Mail: mail, + SSHPublicKey: sshPublicKey, + IsAdmin: isAdmin, + IsRestricted: isRestricted, + LdapTeamAdd: teamsToAdd, + LdapTeamRemove: teamsToRemove, } } @@ -443,7 +529,7 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) { var isAttributeSSHPublicKeySet = len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0 - attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail} + attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.UserUID} if isAttributeSSHPublicKeySet { attribs = append(attribs, ls.AttributeSSHPublicKey) } @@ -467,12 +553,23 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) { result := make([]*SearchResult, len(sr.Entries)) for i, v := range sr.Entries { + teamsToAdd := make(map[string][]string) + teamsToRemove := make(map[string][]string) + if ls.TeamGroupMapEnabled || ls.TeamGroupMapRemoval { + userAttributeListedInGroup := v.GetAttributeValue(ls.UserUID) + if ls.UserUID == "dn" || ls.UserUID == "DN" { + userAttributeListedInGroup = v.DN + } + teamsToAdd, teamsToRemove = ls.getMappedTeams(l, userAttributeListedInGroup) + } result[i] = &SearchResult{ - Username: v.GetAttributeValue(ls.AttributeUsername), - Name: v.GetAttributeValue(ls.AttributeName), - Surname: v.GetAttributeValue(ls.AttributeSurname), - Mail: v.GetAttributeValue(ls.AttributeMail), - IsAdmin: checkAdmin(l, ls, v.DN), + Username: v.GetAttributeValue(ls.AttributeUsername), + Name: v.GetAttributeValue(ls.AttributeName), + Surname: v.GetAttributeValue(ls.AttributeSurname), + Mail: v.GetAttributeValue(ls.AttributeMail), + IsAdmin: checkAdmin(l, ls, v.DN), + LdapTeamAdd: teamsToAdd, + LdapTeamRemove: teamsToRemove, } if !result[i].IsAdmin { result[i].IsRestricted = checkRestricted(l, ls, v.DN) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7f58a11e73852..666a6dce5d459 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2405,6 +2405,9 @@ auths.group_search_base = Group Search Base DN auths.valid_groups_filter = Valid Groups Filter auths.group_attribute_list_users = Group Attribute Containing List Of Users auths.user_attribute_in_group = User Attribute Listed In Group +auths.team_group_map = Map LDAP groups to Organization teams +auths.team_group_map_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group +auths.team_group_map_enabled = Enable mapping LDAP groups to gitea organizations teams auths.ms_ad_sa = MS AD Search Attributes auths.smtp_auth = SMTP Authentication Type auths.smtphost = SMTP Host diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index a2f9ab0a5c326..905476a76d5cc 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -145,6 +145,9 @@ func parseLDAPConfig(form forms.AuthenticationForm) *models.LDAPConfig { AdminFilter: form.AdminFilter, RestrictedFilter: form.RestrictedFilter, AllowDeactivateAll: form.AllowDeactivateAll, + TeamGroupMap: form.TeamGroupMap, + TeamGroupMapRemoval: form.TeamGroupMapRemoval, + TeamGroupMapEnabled: form.TeamGroupMapEnabled, Enabled: true, }, } diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 30621cadffc2f..5cdf7b3c4e16b 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -67,6 +67,9 @@ type AuthenticationForm struct { SSPIStripDomainNames bool SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"` SSPIDefaultLanguage string + TeamGroupMap string + TeamGroupMapRemoval bool + TeamGroupMapEnabled bool } // Validate validates fields diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index d825cd7d12de6..0cf6aa49209b9 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -98,10 +98,22 @@ +
+
+ + +
+
+
+
+ + +
+
- +
@@ -109,10 +121,6 @@
-
- - -
@@ -123,6 +131,14 @@

+
+ + +
+
+ + +
{{if .Source.IsLDAP}}
diff --git a/templates/admin/auth/source/ldap.tmpl b/templates/admin/auth/source/ldap.tmpl index 1cbcb2fd415ea..94d527d81cf71 100644 --- a/templates/admin/auth/source/ldap.tmpl +++ b/templates/admin/auth/source/ldap.tmpl @@ -70,10 +70,22 @@
+
+
+ + +
+
+
+
+ + +
+
- +
@@ -81,10 +93,6 @@
-
- - -
@@ -95,6 +103,14 @@

+
+ + +
+
+ + +
diff --git a/vendor/github.com/thoas/go-funk/.gitignore b/vendor/github.com/thoas/go-funk/.gitignore new file mode 100644 index 0000000000000..7b4c598b0b9fb --- /dev/null +++ b/vendor/github.com/thoas/go-funk/.gitignore @@ -0,0 +1,27 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +#GoLand +.idea \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/.travis.yml b/vendor/github.com/thoas/go-funk/.travis.yml new file mode 100644 index 0000000000000..dbc0f7267f8e5 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/.travis.yml @@ -0,0 +1,7 @@ +language: go +before_install: + - go get golang.org/x/tools/cmd/cover + - go get github.com/stretchr/testify +go: + - "1.14" +script: make test diff --git a/vendor/github.com/thoas/go-funk/CHANGELOG.md b/vendor/github.com/thoas/go-funk/CHANGELOG.md new file mode 100644 index 0000000000000..000a371bf4291 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/CHANGELOG.md @@ -0,0 +1,29 @@ +go-funk changelog +================= + +0.1 (2017-01-18) +---------------- + +Changes can be seen [here](https://github.com/thoas/go-funk/compare/73b8ae1f6443c9d4acbdc612bbb2ca804bb39b1d...master) + +* Better test suite +* Better documentation +* Add typesafe implementations: + + * ``Contains`` + * ``Sum`` + * ``Reverse`` + * ``IndexOf`` + * ``Uniq`` + * ``Shuffle`` +* Add benchmarks + + * ``Contains`` + * ``Uniq`` + * ``Sum`` +* Fix ``redirectValue`` when using a circular reference +* Add ``Sum`` generic implementation which computes the sum of values in an array +* Add ``Tail`` generic implementation to retrieve all but the first element of array +* Add ``Initial`` generic implementation to retrieve all but the last element of array +* Add ``Last`` generic implementation to retrieve the last element of an array +* Add ``Head`` generic implementation to retrieve the first element of an array diff --git a/vendor/github.com/thoas/go-funk/LICENSE b/vendor/github.com/thoas/go-funk/LICENSE new file mode 100644 index 0000000000000..2430978a06793 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Florent Messa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/thoas/go-funk/Makefile b/vendor/github.com/thoas/go-funk/Makefile new file mode 100644 index 0000000000000..2fed2b2e54aff --- /dev/null +++ b/vendor/github.com/thoas/go-funk/Makefile @@ -0,0 +1,5 @@ +test: + go test -v + +bench: + go test -benchmem -bench . diff --git a/vendor/github.com/thoas/go-funk/README.rst b/vendor/github.com/thoas/go-funk/README.rst new file mode 100644 index 0000000000000..2f6b539874c2f --- /dev/null +++ b/vendor/github.com/thoas/go-funk/README.rst @@ -0,0 +1,835 @@ +go-funk +======= + +.. image:: https://secure.travis-ci.org/thoas/go-funk.svg?branch=master + :alt: Build Status + :target: http://travis-ci.org/thoas/go-funk + +.. image:: https://godoc.org/github.com/thoas/go-funk?status.svg + :alt: GoDoc + :target: https://pkg.go.dev/github.com/thoas/go-funk + +.. image:: https://goreportcard.com/badge/github.com/thoas/go-funk + :alt: Go report + :target: https://goreportcard.com/report/github.com/thoas/go-funk + +``go-funk`` is a modern Go library based on reflect_. + +Generic helpers rely on reflect_, be careful this code runs exclusively on runtime so you must have a good test suite. + +These helpers have started as an experiment to learn reflect_. It may look like lodash_ in some aspects but +it will have its own roadmap. lodash_ is an awesome library with a lot of work behind it, all features included in +``go-funk`` come from internal use cases. + +You can also find typesafe implementation in the godoc_. + +Why this name? +-------------- + +Long story, short answer because ``func`` is a reserved word in Go, I wanted something similar. + +Initially this project was named ``fn`` I don't need to explain why that was a bad idea for french speakers :) + +Let's ``funk``! + +.. image:: https://media.giphy.com/media/3oEjHQKtDXpeGN9rW0/giphy.gif + +<3 + +Installation +------------ + +.. code-block:: bash + + go get github.com/thoas/go-funk + +Usage +----- + +.. code-block:: go + + import "github.com/thoas/go-funk" + +These examples will be based on the following data model: + +.. code-block:: go + + type Foo struct { + ID int + FirstName string `tag_name:"tag 1"` + LastName string `tag_name:"tag 2"` + Age int `tag_name:"tag 3"` + } + + func (f Foo) TableName() string { + return "foo" + } + +With fixtures: + +.. code-block:: go + + f := &Foo{ + ID: 1, + FirstName: "Foo", + LastName: "Bar", + Age: 30, + } + +You can import ``go-funk`` using a basic statement: + +.. code-block:: go + + import "github.com/thoas/go-funk" + +funk.Contains +............. + +Returns true if an element is present in a iteratee (slice, map, string). + +One frustrating thing in Go is to implement ``contains`` methods for each type, for example: + +.. code-block:: go + + func ContainsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false + } + +this can be replaced by ``funk.Contains``: + +.. code-block:: go + + // slice of string + funk.Contains([]string{"foo", "bar"}, "bar") // true + + // slice of Foo ptr + funk.Contains([]*Foo{f}, f) // true + funk.Contains([]*Foo{f}, func (foo *Foo) bool { + return foo.ID == f.ID + }) // true + funk.Contains([]*Foo{f}, nil) // false + + b := &Foo{ + ID: 2, + FirstName: "Florent", + LastName: "Messa", + Age: 28, + } + + funk.Contains([]*Foo{f}, b) // false + + // string + funk.Contains("florent", "rent") // true + funk.Contains("florent", "foo") // false + + // even map + funk.Contains(map[int]string{1: "Florent"}, 1) // true + funk.Contains(map[int]string{1: "Florent"}, func(key int, name string) bool { + return key == 1 // or `name == "Florent"` for the value type + }) // true + +see also, typesafe implementations: ContainsInt_, ContainsInt64_, ContainsFloat32_, ContainsFloat64_, ContainsString_ + +.. _ContainsFloat32: https://godoc.org/github.com/thoas/go-funk#ContainsFloat32 +.. _ContainsFloat64: https://godoc.org/github.com/thoas/go-funk#ContainsFloat64 +.. _ContainsInt: https://godoc.org/github.com/thoas/go-funk#ContainsInt +.. _ContainsInt64: https://godoc.org/github.com/thoas/go-funk#ContainsInt64 +.. _ContainsString: https://godoc.org/github.com/thoas/go-funk#ContainsString + +funk.Intersect +.............. + +Returns the intersection between two collections. + +.. code-block:: go + + funk.Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{2, 4} + funk.Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"foo", "bar"} + +see also, typesafe implementations: IntersectString + +.. IntersectString: https://godoc.org/github.com/thoas/go-funk#IntersectString + + +funk.Difference +.............. + +Returns the difference between two collections. + +.. code-block:: go + + funk.Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{1, 3}, []int{6} + funk.Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"hello"}, []string{} + +see also, typesafe implementations: DifferenceString + +.. DifferenceString: https://godoc.org/github.com/thoas/go-funk#DifferenceString + + +funk.IndexOf +............ + +Gets the index at which the first occurrence of a value is found in an array or return -1 +if the value cannot be found. + +.. code-block:: go + + // slice of string + funk.IndexOf([]string{"foo", "bar"}, "bar") // 1 + funk.IndexOf([]string{"foo", "bar"}, func(value string) bool { + return value == "bar" + }) // 1 + funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1 + +see also, typesafe implementations: IndexOfInt_, IndexOfInt64_, IndexOfFloat32_, IndexOfFloat64_, IndexOfString_ + +.. _IndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat32 +.. _IndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat64 +.. _IndexOfInt: https://godoc.org/github.com/thoas/go-funk#IndexOfInt +.. _IndexOfInt64: https://godoc.org/github.com/thoas/go-funk#IndexOfInt64 +.. _IndexOfString: https://godoc.org/github.com/thoas/go-funk#IndexOfString + +funk.LastIndexOf +................ + +Gets the index at which the last occurrence of a value is found in an array or return -1 +if the value cannot be found. + +.. code-block:: go + + // slice of string + funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2 + funk.LastIndexOf([]string{"foo", "bar"}, func(value string) bool { + return value == "bar" + }) // 2 + funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1 + +see also, typesafe implementations: LastIndexOfInt_, LastIndexOfInt64_, LastIndexOfFloat32_, LastIndexOfFloat64_, LastIndexOfString_ + +.. _LastIndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat32 +.. _LastIndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat64 +.. _LastIndexOfInt: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt +.. _LastIndexOfInt64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt64 +.. _LastIndexOfString: https://godoc.org/github.com/thoas/go-funk#LastIndexOfString + +funk.ToMap +.......... + +Transforms a slice of structs to a map based on a ``pivot`` field. + +.. code-block:: go + + f := &Foo{ + ID: 1, + FirstName: "Gilles", + LastName: "Fabio", + Age: 70, + } + + b := &Foo{ + ID: 2, + FirstName: "Florent", + LastName: "Messa", + Age: 80, + } + + results := []*Foo{f, b} + + mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b} + +funk.Filter +........... + +Filters a slice based on a predicate. + +.. code-block:: go + + r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool { + return x%2 == 0 + }) // []int{2, 4} + +see also, typesafe implementations: FilterInt_, FilterInt64_, FilterFloat32_, FilterFloat64_, FilterString_ + +.. _FilterFloat32: https://godoc.org/github.com/thoas/go-funk#FilterFloat32 +.. _FilterFloat64: https://godoc.org/github.com/thoas/go-funk#FilterFloat64 +.. _FilterInt: https://godoc.org/github.com/thoas/go-funk#FilterInt +.. _FilterInt64: https://godoc.org/github.com/thoas/go-funk#FilterInt64 +.. _FilterString: https://godoc.org/github.com/thoas/go-funk#FilterString + +funk.Reduce +........... + +Reduces an iteratee based on an accumulator function or operation rune for numbers. + +.. code-block:: go + + // Using operation runes. '+' and '*' only supported. + r := funk.Reduce([]int{1, 2, 3, 4}, '+', float64(0)) // 10 + r := funk.Reduce([]int{1, 2, 3, 4}, '*', 1) // 24 + + // Using accumulator function + r := funk.Reduce([]int{1, 2, 3, 4}, func(acc float64, num int) float64 { + return acc + float64(num) + }, float64(0)) // 10 + + r := funk.Reduce([]int{1, 2, 3, 4}, func(acc string, num int) string { + return acc + fmt.Sprint(num) + }, "") // "1234" + +funk.Find +......... + +Finds an element in a slice based on a predicate. + +.. code-block:: go + + r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool { + return x%2 == 0 + }) // 2 + +see also, typesafe implementations: FindInt_, FindInt64_, FindFloat32_, FindFloat64_, FindString_ + +.. _FindFloat32: https://godoc.org/github.com/thoas/go-funk#FindFloat32 +.. _FindFloat64: https://godoc.org/github.com/thoas/go-funk#FindFloat64 +.. _FindInt: https://godoc.org/github.com/thoas/go-funk#FindInt +.. _FindInt64: https://godoc.org/github.com/thoas/go-funk#FindInt64 +.. _FindString: https://godoc.org/github.com/thoas/go-funk#FindString + +funk.Map +........ + +Manipulates an iteratee (map, slice) and transforms it to another type: + +* map -> slice +* map -> map +* slice -> map +* slice -> slice + +.. code-block:: go + + r := funk.Map([]int{1, 2, 3, 4}, func(x int) int { + return x * 2 + }) // []int{2, 4, 6, 8} + + r := funk.Map([]int{1, 2, 3, 4}, func(x int) string { + return "Hello" + }) // []string{"Hello", "Hello", "Hello", "Hello"} + + r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) { + return x, x + }) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4} + + mapping := map[int]string{ + 1: "Florent", + 2: "Gilles", + } + + r = funk.Map(mapping, func(k int, v string) int { + return k + }) // []int{1, 2} + + r = funk.Map(mapping, func(k int, v string) (string, string) { + return fmt.Sprintf("%d", k), v + }) // map[string]string{"1": "Florent", "2": "Gilles"} + +funk.FlatMap +............ + +Manipulates an iteratee (map, slice) and transforms it to to a flattened collection of another type: + +* map -> slice +* slice -> slice + +.. code-block:: go + + r := funk.FlatMap([][]int{{1, 2}, {3, 4}}, func(x []int) []int { + return append(x, 0) + }) // []int{1, 2, 0, 3, 4, 0} + + mapping := map[string][]int{ + "Florent": {1, 2}, + "Gilles": {3, 4}, + } + + r = funk.FlatMap(mapping, func(k string, v []int) []int { + return v + }) // []int{1, 2, 3, 4} + +funk.Get +........ + +Retrieves the value at path of struct(s) or map(s). + +.. code-block:: go + + var bar *Bar = &Bar{ + Name: "Test", + Bars: []*Bar{ + &Bar{ + Name: "Level1-1", + Bar: &Bar{ + Name: "Level2-1", + }, + }, + &Bar{ + Name: "Level1-2", + Bar: &Bar{ + Name: "Level2-2", + }, + }, + }, + } + + var foo *Foo = &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + Bar: bar, + Bars: []*Bar{ + bar, + bar, + }, + } + + funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} + funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} + funk.Get(foo, "Bar.Name") // Test + +``funk.Get`` also support ``map`` values: + +.. code-block:: go + + bar := map[string]interface{}{ + "Name": "Test", + } + + foo1 := map[string]interface{}{ + "ID": 1, + "FirstName": "Dark", + "LastName": "Vador", + "Age": 30, + "Bar": bar, + } + + foo2 := &map[string]interface{}{ + "ID": 1, + "FirstName": "Dark", + "LastName": "Vador", + "Age": 30, + } // foo2.Bar is nil + + funk.Get(bar, "Name") // "Test" + funk.Get([]map[string]interface{}{foo1, foo2}, "Bar.Name") // []string{"Test"} + funk.Get(foo2, "Bar.Name") // nil + + +``funk.Get`` also handles ``nil`` values: + +.. code-block:: go + + bar := &Bar{ + Name: "Test", + } + + foo1 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + Bar: bar, + } + + foo2 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } // foo2.Bar is nil + + funk.Get([]*Foo{foo1, foo2}, "Bar.Name") // []string{"Test"} + funk.Get(foo2, "Bar.Name") // nil + + + +funk.GetOrElse +.............. + +Retrieves the value of the pointer or default. + +.. code-block:: go + + str := "hello world" + GetOrElse(&str, "foobar") // string{"hello world"} + GetOrElse(str, "foobar") // string{"hello world"} + GetOrElse(nil, "foobar") // string{"foobar"} + +funk.Set +........ +Set value at a path of a struct + +.. code-block:: go + + var bar Bar = Bar{ + Name: "level-0", + Bar: &Bar{ + Name: "level-1", + Bars: []*Bar{ + {Name: "level2-1"}, + {Name: "level2-2"}, + }, + }, + } + + _ = Set(&bar, "level-0-new", "Name") + fmt.Println(bar.Name) // "level-0-new" + + MustSet(&bar, "level-1-new", "Bar.Name") + fmt.Println(bar.Bar.Name) // "level-1-new" + + Set(&bar, "level-2-new", "Bar.Bars.Name") + fmt.Println(bar.Bar.Bars[0].Name) // "level-2-new" + fmt.Println(bar.Bar.Bars[1].Name) // "level-2-new" + +funk.MustSet +............ +Short hand for funk.Set if struct does not contain interface{} field type to discard errors. + +funk.Prune +.......... +Copy a struct with only selected fields. Slice is handled by pruning all elements. + +.. code-block:: go + + bar := &Bar{ + Name: "Test", + } + + foo1 := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Bar: bar, + } + + pruned, _ := Prune(foo1, []string{"FirstName", "Bar.Name"}) + // *Foo{ + // ID: 0, + // FirstName: "Dark", + // LastName: "", + // Bar: &Bar{Name: "Test}, + // } + +funk.PruneByTag +.......... +Same functionality as funk.Prune, but uses struct tags instead of struct field names. + +funk.Keys +......... + +Creates an array of the own enumerable map keys or struct field names. + +.. code-block:: go + + funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed) + + foo := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } + + funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed) + +funk.Values +........... + +Creates an array of the own enumerable map values or struct field values. + +.. code-block:: go + + funk.Values(map[string]int{"one": 1, "two": 2}) // []string{1, 2} (iteration order is not guaranteed) + + foo := &Foo{ + ID: 1, + FirstName: "Dark", + LastName: "Vador", + Age: 30, + } + + funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed) + +funk.ForEach +............ + +Range over an iteratee (map, slice). + +.. code-block:: go + + funk.ForEach([]int{1, 2, 3, 4}, func(x int) { + fmt.Println(x) + }) + +funk.ForEachRight +............ + +Range over an iteratee (map, slice) from the right. + +.. code-block:: go + + results := []int{} + + funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) { + results = append(results, x) + }) + + fmt.Println(results) // []int{4, 3, 2, 1} + +funk.Chunk +.......... + +Creates an array of elements split into groups with the length of the size. +If array can't be split evenly, the final chunk will be the remaining element. + +.. code-block:: go + + funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}} + +funk.FlattenDeep +................ + +Recursively flattens an array. + +.. code-block:: go + + funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4} + +funk.Uniq +......... + +Creates an array with unique values. + +.. code-block:: go + + funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12} + +see also, typesafe implementations: UniqInt_, UniqInt64_, UniqFloat32_, UniqFloat64_, UniqString_ + +.. _UniqFloat32: https://godoc.org/github.com/thoas/go-funk#UniqFloat32 +.. _UniqFloat64: https://godoc.org/github.com/thoas/go-funk#UniqFloat64 +.. _UniqInt: https://godoc.org/github.com/thoas/go-funk#UniqInt +.. _UniqInt64: https://godoc.org/github.com/thoas/go-funk#UniqInt64 +.. _UniqString: https://godoc.org/github.com/thoas/go-funk#UniqString + +funk.Drop +......... + +Creates an array/slice with `n` elements dropped from the beginning. + +.. code-block:: go + + funk.Drop([]int{0, 0, 0, 0}, 3) // []int{0} + +see also, typesafe implementations: DropInt_, DropInt32_, DropInt64_, DropFloat32_, DropFloat64_, DropString_ + +.. _DropInt: https://godoc.org/github.com/thoas/go-funk#DropInt +.. _DropInt32: https://godoc.org/github.com/thoas/go-funk#DropInt64 +.. _DropInt64: https://godoc.org/github.com/thoas/go-funk#DropInt64 +.. _DropFloat32: https://godoc.org/github.com/thoas/go-funk#DropFloat32 +.. _DropFloat64: https://godoc.org/github.com/thoas/go-funk#DropFloat64 +.. _DropString: https://godoc.org/github.com/thoas/go-funk#DropString + +funk.Initial +............ + +Gets all but the last element of array. + +.. code-block:: go + + funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3} + +funk.Tail +......... + +Gets all but the first element of array. + +.. code-block:: go + + funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4} + +funk.Shuffle +............ + +Creates an array of shuffled values. + +.. code-block:: go + + funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0} + + +see also, typesafe implementations: ShuffleInt_, ShuffleInt64_, ShuffleFloat32_, ShuffleFloat64_, ShuffleString_ + +.. _ShuffleFloat32: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat32 +.. _ShuffleFloat64: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat64 +.. _ShuffleInt: https://godoc.org/github.com/thoas/go-funk#ShuffleInt +.. _ShuffleInt64: https://godoc.org/github.com/thoas/go-funk#ShuffleInt64 +.. _ShuffleString: https://godoc.org/github.com/thoas/go-funk#ShuffleString + +funk.Subtract +............. + +Returns the subtraction between two collections. It preserve order. + +.. code-block:: go + + funk.Subtract([]int{0, 1, 2, 3, 4}, []int{0, 4}) // []int{1, 2, 3} + funk.Subtract([]int{0, 3, 2, 3, 4}, []int{0, 4}) // []int{3, 2, 3} + + +see also, typesafe implementations: SubtractString_ + +.. SubtractString: https://godoc.org/github.com/thoas/go-funk#SubtractString + +funk.Sum +........ + +Computes the sum of the values in an array. + +.. code-block:: go + + funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0 + funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5 + +see also, typesafe implementations: SumInt_, SumInt64_, SumFloat32_, SumFloat64_ + +.. _SumFloat32: https://godoc.org/github.com/thoas/go-funk#SumFloat32 +.. _SumFloat64: https://godoc.org/github.com/thoas/go-funk#SumFloat64 +.. _SumInt: https://godoc.org/github.com/thoas/go-funk#SumInt +.. _SumInt64: https://godoc.org/github.com/thoas/go-funk#SumInt64 + +funk.Reverse +............ + +Transforms an array such that the first element will become the last, the second element +will become the second to last, etc. + +.. code-block:: go + + funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0} + +see also, typesafe implementations: ReverseInt_, ReverseInt64_, ReverseFloat32_, ReverseFloat64_, ReverseString_, ReverseStrings_ + +.. _ReverseFloat32: https://godoc.org/github.com/thoas/go-funk#ReverseFloat32 +.. _ReverseFloat64: https://godoc.org/github.com/thoas/go-funk#ReverseFloat64 +.. _ReverseInt: https://godoc.org/github.com/thoas/go-funk#ReverseInt +.. _ReverseInt64: https://godoc.org/github.com/thoas/go-funk#ReverseInt64 +.. _ReverseString: https://godoc.org/github.com/thoas/go-funk#ReverseString +.. _ReverseStrings: https://godoc.org/github.com/thoas/go-funk#ReverseStrings + +funk.SliceOf +............ + +Returns a slice based on an element. + +.. code-block:: go + + funk.SliceOf(f) // will return a []*Foo{f} + +funk.RandomInt +.............. + +Generates a random int, based on a min and max values. + +.. code-block:: go + + funk.RandomInt(0, 100) // will be between 0 and 100 + +funk.RandomString +................. + +Generates a random string with a fixed length. + +.. code-block:: go + + funk.RandomString(4) // will be a string of 4 random characters + +funk.Shard +.......... + +Generates a sharded string with a fixed length and depth. + +.. code-block:: go + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"} + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"} + + funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 3, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"} + +funk.Subset +............. + +Returns true if a collection is a subset of another + +.. code-block:: go + + funk.Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) // true + funk.Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) //true + + +Performance +----------- + +``go-funk`` currently has an open issue about performance_, don't hesitate to participate in the discussion +to enhance the generic helpers implementations. + +Let's stop beating around the bush, a typesafe implementation in pure Go of ``funk.Contains``, let's say for example: + +.. code-block:: go + + func ContainsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false + } + +will always outperform an implementation based on reflect_ in terms of speed and allocs because of +how it's implemented in the language. + +If you want a similarity, gorm_ will always be slower than sqlx_ (which is very low level btw) and will use more allocs. + +You must not think generic helpers of ``go-funk`` as a replacement when you are dealing with performance in your codebase, +you should use typesafe implementations instead. + +Contributing +------------ + +* Ping me on twitter `@thoas `_ (DMs, mentions, whatever :)) +* Fork the `project `_ +* Fix `open issues `_ or request new features + +Don't hesitate ;) + +Authors +------- + +* Florent Messa +* Gilles Fabio +* Alexey Pokhozhaev +* Alexandre Nicolaie + +.. _reflect: https://golang.org/pkg/reflect/ +.. _lodash: https://lodash.com/ +.. _performance: https://github.com/thoas/go-funk/issues/19 +.. _gorm: https://github.com/jinzhu/gorm +.. _sqlx: https://github.com/jmoiron/sqlx +.. _godoc: https://godoc.org/github.com/thoas/go-funk diff --git a/vendor/github.com/thoas/go-funk/assign.go b/vendor/github.com/thoas/go-funk/assign.go new file mode 100644 index 0000000000000..e35c7500006d1 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/assign.go @@ -0,0 +1,129 @@ +package funk + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// Set assigns in at path with value val. i.e. in.path = val +// in accepts types of ptr to struct, ptr to variable, slice and ptr to slice. +// Along the path, interface{} is supported and nil ptr is initialized to ptr to zero value +// of the type until the variable to be set is obtained. +// It returns errors when encountering along the path unknown types, uninitialized +// interface{} or interface{} containing struct directly (not ptr to struct). +// +// Slice is resolved the same way in funk.Get(), by traversing each element of the slice, +// so that each element of the slice's corresponding field are going to be set to the same provided val. +// If Set is called on slice with empty path "", it behaves the same as funk.Fill() +// +// If in is well formed, i.e. do not expect above descripted errors to happen, funk.MustSet() +// is a short hand wrapper to discard error return +func Set(in interface{}, val interface{}, path string) error { + if in == nil { + return errors.New("Cannot Set nil") + } + parts := []string{} + if path != "" { + parts = strings.Split(path, ".") + } + return setByParts(in, val, parts) +} + +// we need this layer to handle interface{} type +func setByParts(in interface{}, val interface{}, parts []string) error { + + if in == nil { + // nil interface can happen during traversing the path + return errors.New("Cannot traverse nil/uninitialized interface{}") + } + + inValue := reflect.ValueOf(in) + inKind := inValue.Type().Kind() + + // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. + // I.e. it is not CanAddr() or CanSet() + // So we require in interface{} to be a ptr, slice or array + if inKind == reflect.Ptr { + inValue = inValue.Elem() // if it is ptr we set its content not ptr its self + } else if inKind != reflect.Array && inKind != reflect.Slice { + return fmt.Errorf("Type %s not supported by Set", inValue.Type().String()) + } + + return set(inValue, reflect.ValueOf(val), parts) +} + +// traverse inValue using path in parts and set the dst to be setValue +func set(inValue reflect.Value, setValue reflect.Value, parts []string) error { + + // traverse the path to get the inValue we need to set + i := 0 + for i < len(parts) { + + kind := inValue.Kind() + + switch kind { + case reflect.Invalid: + // do not expect this case to happen + return errors.New("nil pointer found along the path") + case reflect.Struct: + fValue := inValue.FieldByName(parts[i]) + if !fValue.IsValid() { + return fmt.Errorf("field name %v is not found in struct %v", parts[i], inValue.Type().String()) + } + if !fValue.CanSet() { + return fmt.Errorf("field name %v is not exported in struct %v", parts[i], inValue.Type().String()) + } + inValue = fValue + i++ + case reflect.Slice | reflect.Array: + // set all its elements + length := inValue.Len() + for j := 0; j < length; j++ { + err := set(inValue.Index(j), setValue, parts[i:]) + if err != nil { + return err + } + } + return nil + case reflect.Ptr: + // only traverse down one level + if inValue.IsNil() { + // we initialize nil ptr to ptr to zero value of the type + // and continue traversing + inValue.Set(reflect.New(inValue.Type().Elem())) + } + // traverse the ptr until it is not pointer any more or is nil again + inValue = redirectValue(inValue) + case reflect.Interface: + // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. + // I.e. it is not CanAddr() or CanSet(). This is why setByParts has a nil ptr check. + // we treat this as a new call to setByParts, and it will do proper check of the types + return setByParts(inValue.Interface(), setValue.Interface(), parts[i:]) + default: + return fmt.Errorf("kind %v in path %v is not supported", kind, parts[i]) + } + + } + // here inValue holds the value we need to set + + // interface{} can be set to any val + // other types we ensure the type matches + if inValue.Kind() != setValue.Kind() && inValue.Kind() != reflect.Interface { + return fmt.Errorf("cannot set target of type %v with type %v", inValue.Kind(), setValue.Kind()) + } + inValue.Set(setValue) + + return nil +} + +// MustSet is functionally the same as Set. +// It panics instead of returning error. +// It is safe to use if the in value is well formed. +func MustSet(in interface{}, val interface{}, path string) { + err := Set(in, val, path) + if err != nil { + panic(err) + } +} diff --git a/vendor/github.com/thoas/go-funk/builder.go b/vendor/github.com/thoas/go-funk/builder.go new file mode 100644 index 0000000000000..6dfc814d73241 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/builder.go @@ -0,0 +1,110 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// Builder contains all tools which can be chained. +type Builder interface { + Chunk(size int) Builder + Compact() Builder + Drop(n int) Builder + Filter(predicate interface{}) Builder + Flatten() Builder + FlattenDeep() Builder + Initial() Builder + Intersect(y interface{}) Builder + Join(rarr interface{}, fnc JoinFnc) Builder + Map(mapFunc interface{}) Builder + FlatMap(mapFunc interface{}) Builder + Reverse() Builder + Shuffle() Builder + Tail() Builder + Uniq() Builder + Without(values ...interface{}) Builder + + All() bool + Any() bool + Contains(elem interface{}) bool + Every(elements ...interface{}) bool + Find(predicate interface{}) interface{} + ForEach(predicate interface{}) + ForEachRight(predicate interface{}) + Head() interface{} + Keys() interface{} + IndexOf(elem interface{}) int + IsEmpty() bool + Last() interface{} + LastIndexOf(elem interface{}) int + NotEmpty() bool + Product() float64 + Reduce(reduceFunc, acc interface{}) interface{} + Sum() float64 + Type() reflect.Type + Value() interface{} + Values() interface{} +} + +// Chain creates a simple new go-funk.Builder from a collection. Each method +// call generate a new builder containing the previous result. +func Chain(v interface{}) Builder { + isNotNil(v, "Chain") + + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return &chainBuilder{v} + } + + panic(fmt.Sprintf("Type %s is not supported by Chain", valueType.String())) +} + +// LazyChain creates a lazy go-funk.Builder from a collection. Each method call +// generate a new builder containing a method generating the previous value. +// With that, all data are only generated when we call a tailling method like All or Find. +func LazyChain(v interface{}) Builder { + isNotNil(v, "LazyChain") + + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return &lazyBuilder{func() interface{} { return v }} + } + + panic(fmt.Sprintf("Type %s is not supported by LazyChain", valueType.String())) + +} + +// LazyChainWith creates a lazy go-funk.Builder from a generator. Like LazyChain, each +// method call generate a new builder containing a method generating the previous value. +// But, instead of using a collection, it takes a generator which can generate values. +// With LazyChainWith, to can create a generic pipeline of collection transformation and, +// throw the generator, sending different collection. +func LazyChainWith(generator func() interface{}) Builder { + isNotNil(generator, "LazyChainWith") + return &lazyBuilder{func() interface{} { + isNotNil(generator, "LazyChainWith") + + v := generator() + valueType := reflect.TypeOf(v) + if isValidBuilderEntry(valueType) || + (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { + return v + } + + panic(fmt.Sprintf("Type %s is not supported by LazyChainWith generator", valueType.String())) + }} +} + +func isNotNil(v interface{}, from string) { + if v == nil { + panic(fmt.Sprintf("nil value is not supported by %s", from)) + } +} + +func isValidBuilderEntry(valueType reflect.Type) bool { + return valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array || + valueType.Kind() == reflect.Map || + valueType.Kind() == reflect.String +} diff --git a/vendor/github.com/thoas/go-funk/chain_builder.go b/vendor/github.com/thoas/go-funk/chain_builder.go new file mode 100644 index 0000000000000..18226ab573c51 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/chain_builder.go @@ -0,0 +1,142 @@ +package funk + +import ( + "fmt" + "reflect" +) + +type chainBuilder struct { + collection interface{} +} + +func (b *chainBuilder) Chunk(size int) Builder { + return &chainBuilder{Chunk(b.collection, size)} +} +func (b *chainBuilder) Compact() Builder { + return &chainBuilder{Compact(b.collection)} +} +func (b *chainBuilder) Drop(n int) Builder { + return &chainBuilder{Drop(b.collection, n)} +} +func (b *chainBuilder) Filter(predicate interface{}) Builder { + return &chainBuilder{Filter(b.collection, predicate)} +} +func (b *chainBuilder) Flatten() Builder { + return &chainBuilder{Flatten(b.collection)} +} +func (b *chainBuilder) FlattenDeep() Builder { + return &chainBuilder{FlattenDeep(b.collection)} +} +func (b *chainBuilder) Initial() Builder { + return &chainBuilder{Initial(b.collection)} +} +func (b *chainBuilder) Intersect(y interface{}) Builder { + return &chainBuilder{Intersect(b.collection, y)} +} +func (b *chainBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { + return &chainBuilder{Join(b.collection, rarr, fnc)} +} +func (b *chainBuilder) Map(mapFunc interface{}) Builder { + return &chainBuilder{Map(b.collection, mapFunc)} +} +func (b *chainBuilder) FlatMap(mapFunc interface{}) Builder { + return &chainBuilder{FlatMap(b.collection, mapFunc)} +} +func (b *chainBuilder) Reverse() Builder { + return &chainBuilder{Reverse(b.collection)} +} +func (b *chainBuilder) Shuffle() Builder { + return &chainBuilder{Shuffle(b.collection)} +} +func (b *chainBuilder) Tail() Builder { + return &chainBuilder{Tail(b.collection)} +} +func (b *chainBuilder) Uniq() Builder { + return &chainBuilder{Uniq(b.collection)} +} +func (b *chainBuilder) Without(values ...interface{}) Builder { + return &chainBuilder{Without(b.collection, values...)} +} + +func (b *chainBuilder) All() bool { + v := reflect.ValueOf(b.collection) + t := v.Type() + + if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { + panic(fmt.Sprintf("Type %s is not supported by Chain.All", t.String())) + } + + c := make([]interface{}, v.Len()) + for i := 0; i < v.Len(); i++ { + c[i] = v.Index(i).Interface() + } + return All(c...) +} +func (b *chainBuilder) Any() bool { + v := reflect.ValueOf(b.collection) + t := v.Type() + + if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { + panic(fmt.Sprintf("Type %s is not supported by Chain.Any", t.String())) + } + + c := make([]interface{}, v.Len()) + for i := 0; i < v.Len(); i++ { + c[i] = v.Index(i).Interface() + } + return Any(c...) +} +func (b *chainBuilder) Contains(elem interface{}) bool { + return Contains(b.collection, elem) +} +func (b *chainBuilder) Every(elements ...interface{}) bool { + return Every(b.collection, elements...) +} +func (b *chainBuilder) Find(predicate interface{}) interface{} { + return Find(b.collection, predicate) +} +func (b *chainBuilder) ForEach(predicate interface{}) { + ForEach(b.collection, predicate) +} +func (b *chainBuilder) ForEachRight(predicate interface{}) { + ForEachRight(b.collection, predicate) +} +func (b *chainBuilder) Head() interface{} { + return Head(b.collection) +} +func (b *chainBuilder) Keys() interface{} { + return Keys(b.collection) +} +func (b *chainBuilder) IndexOf(elem interface{}) int { + return IndexOf(b.collection, elem) +} +func (b *chainBuilder) IsEmpty() bool { + return IsEmpty(b.collection) +} +func (b *chainBuilder) Last() interface{} { + return Last(b.collection) +} +func (b *chainBuilder) LastIndexOf(elem interface{}) int { + return LastIndexOf(b.collection, elem) +} +func (b *chainBuilder) NotEmpty() bool { + return NotEmpty(b.collection) +} +func (b *chainBuilder) Product() float64 { + return Product(b.collection) +} +func (b *chainBuilder) Reduce(reduceFunc, acc interface{}) interface{} { + return Reduce(b.collection, reduceFunc, acc) +} +func (b *chainBuilder) Sum() float64 { + return Sum(b.collection) +} +func (b *chainBuilder) Type() reflect.Type { + return reflect.TypeOf(b.collection) +} +func (b *chainBuilder) Value() interface{} { + return b.collection +} +func (b *chainBuilder) Values() interface{} { + return Values(b.collection) +} diff --git a/vendor/github.com/thoas/go-funk/compact.go b/vendor/github.com/thoas/go-funk/compact.go new file mode 100644 index 0000000000000..ba151aa9c25c9 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/compact.go @@ -0,0 +1,50 @@ +package funk + +import ( + "reflect" +) + +// Compact creates a slice with all empty/zero values removed. +func Compact(value interface{}) interface{} { + arr := redirectValue(reflect.ValueOf(value)) + + if arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { + panic("First parameter must be array or slice") + } + + sliceElemType := sliceElem(arr.Type()) + resultSliceType := reflect.SliceOf(sliceElemType) + result := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arr.Len(); i++ { + elemVal := arr.Index(i) + + if elemVal.Kind() == reflect.Interface { + elemVal = elemVal.Elem() + } + + redirectedElemVal := redirectValue(elemVal) + + switch redirectedElemVal.Kind() { + case reflect.Invalid: + continue + case reflect.Func: + if redirectedElemVal.IsNil() { + continue + } + case reflect.Map, reflect.Slice, reflect.Chan: + if redirectedElemVal.Len() == 0 { + continue + } + default: + defaultValue := reflect.Zero(redirectedElemVal.Type()).Interface() + if redirectedElemVal.Interface() == defaultValue { + continue + } + } + + result = reflect.Append(result, elemVal) + } + + return result.Interface() +} diff --git a/vendor/github.com/thoas/go-funk/fill.go b/vendor/github.com/thoas/go-funk/fill.go new file mode 100644 index 0000000000000..6af5cfa77fc93 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/fill.go @@ -0,0 +1,34 @@ +package funk + +import ( + "errors" + "fmt" + "reflect" +) + +// Fill fills elements of array with value +func Fill(in interface{}, fillValue interface{}) (interface{}, error) { + inValue := reflect.ValueOf(in) + inKind := inValue.Type().Kind() + if inKind != reflect.Slice && inKind != reflect.Array { + return nil, errors.New("Can only fill slices and arrays") + } + + inType := reflect.TypeOf(in).Elem() + value := reflect.ValueOf(fillValue) + if inType != value.Type() { + return nil, fmt.Errorf( + "Cannot fill '%s' with '%s'", reflect.TypeOf(in), value.Type(), + ) + } + + length := inValue.Len() + newSlice := reflect.SliceOf(reflect.TypeOf(fillValue)) + in = reflect.MakeSlice(newSlice, length, length).Interface() + inValue = reflect.ValueOf(in) + + for i := 0; i < length; i++ { + inValue.Index(i).Set(value) + } + return in, nil +} diff --git a/vendor/github.com/thoas/go-funk/go.mod b/vendor/github.com/thoas/go-funk/go.mod new file mode 100644 index 0000000000000..23fb24af3e95f --- /dev/null +++ b/vendor/github.com/thoas/go-funk/go.mod @@ -0,0 +1,5 @@ +module github.com/thoas/go-funk + +go 1.13 + +require github.com/stretchr/testify v1.4.0 diff --git a/vendor/github.com/thoas/go-funk/go.sum b/vendor/github.com/thoas/go-funk/go.sum new file mode 100644 index 0000000000000..8fdee5854f19a --- /dev/null +++ b/vendor/github.com/thoas/go-funk/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/thoas/go-funk/helpers.go b/vendor/github.com/thoas/go-funk/helpers.go new file mode 100644 index 0000000000000..da3f032a5d437 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/helpers.go @@ -0,0 +1,317 @@ +package funk + +import ( + "bytes" + "math/rand" + "reflect" +) + +var numericZeros = []interface{}{ + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), +} + +// ToFloat64 converts any numeric value to float64. +func ToFloat64(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// PtrOf makes a copy of the given interface and returns a pointer. +func PtrOf(itf interface{}) interface{} { + t := reflect.TypeOf(itf) + + cp := reflect.New(t) + cp.Elem().Set(reflect.ValueOf(itf)) + + // Avoid double pointers if itf is a pointer + if t.Kind() == reflect.Ptr { + return cp.Elem().Interface() + } + + return cp.Interface() +} + +// IsFunction returns if the argument is a function. +func IsFunction(in interface{}, num ...int) bool { + funcType := reflect.TypeOf(in) + + result := funcType != nil && funcType.Kind() == reflect.Func + + if len(num) >= 1 { + result = result && funcType.NumIn() == num[0] + } + + if len(num) == 2 { + result = result && funcType.NumOut() == num[1] + } + + return result +} + +// IsPredicate returns if the argument is a predicate function. +func IsPredicate(in interface{}, inTypes ...reflect.Type) bool { + if len(inTypes) == 0 { + inTypes = append(inTypes, nil) + } + + funcType := reflect.TypeOf(in) + + result := funcType != nil && funcType.Kind() == reflect.Func + + result = result && funcType.NumOut() == 1 && funcType.Out(0).Kind() == reflect.Bool + result = result && funcType.NumIn() == len(inTypes) + + for i := 0; result && i < len(inTypes); i++ { + inType := inTypes[i] + result = inType == nil || inType.ConvertibleTo(funcType.In(i)) + } + + return result +} + +// IsEqual returns if the two objects are equal +func IsEqual(expected interface{}, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + if exp, ok := expected.([]byte); ok { + act, ok := actual.([]byte) + if !ok { + return false + } + + if exp == nil || act == nil { + return true + } + + return bytes.Equal(exp, act) + } + + return reflect.DeepEqual(expected, actual) + +} + +// IsType returns if the two objects are in the same type +func IsType(expected interface{}, actual interface{}) bool { + return IsEqual(reflect.TypeOf(expected), reflect.TypeOf(actual)) +} + +// Equal returns if the two objects are equal +func Equal(expected interface{}, actual interface{}) bool { + return IsEqual(expected, actual) +} + +// NotEqual returns if the two objects are not equal +func NotEqual(expected interface{}, actual interface{}) bool { + return !IsEqual(expected, actual) +} + +// IsIteratee returns if the argument is an iteratee. +func IsIteratee(in interface{}) bool { + if in == nil { + return false + } + arrType := reflect.TypeOf(in) + + kind := arrType.Kind() + + return kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map +} + +// IsCollection returns if the argument is a collection. +func IsCollection(in interface{}) bool { + arrType := reflect.TypeOf(in) + + kind := arrType.Kind() + + return kind == reflect.Array || kind == reflect.Slice +} + +// SliceOf returns a slice which contains the element. +func SliceOf(in interface{}) interface{} { + value := reflect.ValueOf(in) + + sliceType := reflect.SliceOf(reflect.TypeOf(in)) + slice := reflect.New(sliceType) + sliceValue := reflect.MakeSlice(sliceType, 0, 0) + sliceValue = reflect.Append(sliceValue, value) + slice.Elem().Set(sliceValue) + + return slice.Elem().Interface() +} + +// Any returns true if any element of the iterable is not empty. If the iterable is empty, return False. +func Any(objs ...interface{}) bool { + if len(objs) == 0 { + return false + } + + for _, obj := range objs { + if !IsEmpty(obj) { + return true + } + } + + return false +} + +// All returns true if all elements of the iterable are not empty (or if the iterable is empty) +func All(objs ...interface{}) bool { + if len(objs) == 0 { + return true + } + + for _, obj := range objs { + if IsEmpty(obj) { + return false + } + } + + return true +} + +// IsEmpty returns if the object is considered as empty or not. +func IsEmpty(obj interface{}) bool { + if obj == nil || obj == "" || obj == false { + return true + } + + for _, v := range numericZeros { + if obj == v { + return true + } + } + + objValue := reflect.ValueOf(obj) + + switch objValue.Kind() { + case reflect.Map: + fallthrough + case reflect.Slice, reflect.Chan: + return objValue.Len() == 0 + case reflect.Struct: + return reflect.DeepEqual(obj, ZeroOf(obj)) + case reflect.Ptr: + if objValue.IsNil() { + return true + } + + obj = redirectValue(objValue).Interface() + + return reflect.DeepEqual(obj, ZeroOf(obj)) + } + + return false +} + +// IsZero returns if the object is considered as zero value +func IsZero(obj interface{}) bool { + if obj == nil || obj == "" || obj == false { + return true + } + + for _, v := range numericZeros { + if obj == v { + return true + } + } + + return reflect.DeepEqual(obj, ZeroOf(obj)) +} + +// NotEmpty returns if the object is considered as non-empty or not. +func NotEmpty(obj interface{}) bool { + return !IsEmpty(obj) +} + +// ZeroOf returns a zero value of an element. +func ZeroOf(in interface{}) interface{} { + if in == nil { + return nil + } + + return reflect.Zero(reflect.TypeOf(in)).Interface() +} + +// RandomInt generates a random int, based on a min and max values +func RandomInt(min, max int) int { + return min + rand.Intn(max-min) +} + +// Shard will shard a string name +func Shard(str string, width int, depth int, restOnly bool) []string { + var results []string + + for i := 0; i < depth; i++ { + results = append(results, str[(width*i):(width*(i+1))]) + } + + if restOnly { + results = append(results, str[(width*depth):]) + } else { + results = append(results, str) + } + + return results +} + +var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + +// RandomString returns a random string with a fixed length +func RandomString(n int, allowedChars ...[]rune) string { + var letters []rune + + if len(allowedChars) == 0 { + letters = defaultLetters + } else { + letters = allowedChars[0] + } + + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + + return string(b) +} diff --git a/vendor/github.com/thoas/go-funk/intersection.go b/vendor/github.com/thoas/go-funk/intersection.go new file mode 100644 index 0000000000000..17ec69e3ba6c6 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/intersection.go @@ -0,0 +1,253 @@ +package funk + +import ( + "reflect" +) + +// Intersect returns the intersection between two collections. +// +// Deprecated: use Join(x, y, InnerJoin) instead of Intersect, InnerJoin +// implements deduplication mechanism, so verify your code behaviour +// before using it +func Intersect(x interface{}, y interface{}) interface{} { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + hash := map[interface{}]struct{}{} + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + zType := reflect.SliceOf(xType.Elem()) + zSlice := reflect.MakeSlice(zType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + hash[v] = struct{}{} + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + _, ok := hash[v] + if ok { + zSlice = reflect.Append(zSlice, yValue.Index(i)) + } + } + + return zSlice.Interface() +} + +// IntersectString returns the intersection between two collections of string. +func IntersectString(x []string, y []string) []string { + if len(x) == 0 || len(y) == 0 { + return []string{} + } + + set := []string{} + hash := map[string]struct{}{} + + for _, v := range x { + hash[v] = struct{}{} + } + + for _, v := range y { + _, ok := hash[v] + if ok { + set = append(set, v) + } + } + + return set +} + +// Difference returns the difference between two collections. +func Difference(x interface{}, y interface{}) (interface{}, interface{}) { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + leftType := reflect.SliceOf(xType.Elem()) + leftSlice := reflect.MakeSlice(leftType, 0, 0) + rightType := reflect.SliceOf(yType.Elem()) + rightSlice := reflect.MakeSlice(rightType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + if Contains(y, v) == false { + leftSlice = reflect.Append(leftSlice, xValue.Index(i)) + } + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + if Contains(x, v) == false { + rightSlice = reflect.Append(rightSlice, yValue.Index(i)) + } + } + + return leftSlice.Interface(), rightSlice.Interface() +} + +// DifferenceString returns the difference between two collections of strings. +func DifferenceString(x []string, y []string) ([]string, []string) { + leftSlice := []string{} + rightSlice := []string{} + + for _, v := range x { + if ContainsString(y, v) == false { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if ContainsString(x, v) == false { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt64 returns the difference between two collections of int64s. +func DifferenceInt64(x []int64, y []int64) ([]int64, []int64) { + leftSlice := []int64{} + rightSlice := []int64{} + + for _, v := range x { + if ContainsInt64(y, v) == false { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if ContainsInt64(x, v) == false { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt32 returns the difference between two collections of ints32. +func DifferenceInt32(x []int32, y []int32) ([]int32, []int32) { + leftSlice := []int32{} + rightSlice := []int32{} + + for _, v := range x { + if !ContainsInt32(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsInt32(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceInt returns the difference between two collections of ints. +func DifferenceInt(x []int, y []int) ([]int, []int) { + leftSlice := []int{} + rightSlice := []int{} + + for _, v := range x { + if ContainsInt(y, v) == false { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if ContainsInt(x, v) == false { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt returns the difference between two collections of uints. +func DifferenceUInt(x []uint, y []uint) ([]uint, []uint) { + leftSlice := []uint{} + rightSlice := []uint{} + + for _, v := range x { + if !ContainsUInt(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt32 returns the difference between two collections of uints32. +func DifferenceUInt32(x []uint32, y []uint32) ([]uint32, []uint32) { + leftSlice := []uint32{} + rightSlice := []uint32{} + + for _, v := range x { + if !ContainsUInt32(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt32(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} + +// DifferenceUInt64 returns the difference between two collections of uints64. +func DifferenceUInt64(x []uint64, y []uint64) ([]uint64, []uint64) { + leftSlice := []uint64{} + rightSlice := []uint64{} + + for _, v := range x { + if !ContainsUInt64(y, v) { + leftSlice = append(leftSlice, v) + } + } + + for _, v := range y { + if !ContainsUInt64(x, v) { + rightSlice = append(rightSlice, v) + } + } + + return leftSlice, rightSlice +} diff --git a/vendor/github.com/thoas/go-funk/join.go b/vendor/github.com/thoas/go-funk/join.go new file mode 100644 index 0000000000000..6d0e287777de4 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/join.go @@ -0,0 +1,86 @@ +package funk + +import ( + "reflect" +) + +type JoinFnc func(lx, rx reflect.Value) reflect.Value + +// Join combines two collections using the given join method. +func Join(larr, rarr interface{}, fnc JoinFnc) interface{} { + if !IsCollection(larr) { + panic("First parameter must be a collection") + } + if !IsCollection(rarr) { + panic("Second parameter must be a collection") + } + + lvalue := reflect.ValueOf(larr) + rvalue := reflect.ValueOf(rarr) + if NotEqual(lvalue.Type(), rvalue.Type()) { + panic("Parameters must have the same type") + } + + return fnc(lvalue, rvalue).Interface() +} + +// InnerJoin finds and returns matching data from two collections. +func InnerJoin(lx, rx reflect.Value) reflect.Value { + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()+rx.Len()) + rhash := hashSlice(rx) + lhash := make(map[interface{}]struct{}, lx.Len()) + + for i := 0; i < lx.Len(); i++ { + v := lx.Index(i) + _, ok := rhash[v.Interface()] + _, alreadyExists := lhash[v.Interface()] + if ok && !alreadyExists { + lhash[v.Interface()] = struct{}{} + result = reflect.Append(result, v) + } + } + return result +} + +// OuterJoin finds and returns dissimilar data from two collections. +func OuterJoin(lx, rx reflect.Value) reflect.Value { + ljoin := LeftJoin(lx, rx) + rjoin := RightJoin(lx, rx) + + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), ljoin.Len()+rjoin.Len(), ljoin.Len()+rjoin.Len()) + for i := 0; i < ljoin.Len(); i++ { + result.Index(i).Set(ljoin.Index(i)) + } + for i := 0; i < rjoin.Len(); i++ { + result.Index(ljoin.Len() + i).Set(rjoin.Index(i)) + } + + return result +} + +// LeftJoin finds and returns dissimilar data from the first collection (left). +func LeftJoin(lx, rx reflect.Value) reflect.Value { + result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()) + rhash := hashSlice(rx) + + for i := 0; i < lx.Len(); i++ { + v := lx.Index(i) + _, ok := rhash[v.Interface()] + if !ok { + result = reflect.Append(result, v) + } + } + return result +} + +// LeftJoin finds and returns dissimilar data from the second collection (right). +func RightJoin(lx, rx reflect.Value) reflect.Value { return LeftJoin(rx, lx) } + +func hashSlice(arr reflect.Value) map[interface{}]struct{} { + hash := map[interface{}]struct{}{} + for i := 0; i < arr.Len(); i++ { + v := arr.Index(i).Interface() + hash[v] = struct{}{} + } + return hash +} diff --git a/vendor/github.com/thoas/go-funk/join_primitives.go b/vendor/github.com/thoas/go-funk/join_primitives.go new file mode 100644 index 0000000000000..1e449833ba4d1 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/join_primitives.go @@ -0,0 +1,385 @@ +package funk + +type JoinIntFnc func(lx, rx []int) []int + +// JoinInt combines two int collections using the given join method. +func JoinInt(larr, rarr []int, fnc JoinIntFnc) []int { + return fnc(larr, rarr) +} + +// InnerJoinInt finds and returns matching data from two int collections. +func InnerJoinInt(lx, rx []int) []int { + result := make([]int, 0, len(lx)+len(rx)) + rhash := hashSliceInt(rx) + lhash := make(map[int]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt finds and returns dissimilar data from two int collections. +func OuterJoinInt(lx, rx []int) []int { + ljoin := LeftJoinInt(lx, rx) + rjoin := RightJoinInt(lx, rx) + + result := make([]int, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt finds and returns dissimilar data from the first int collection (left). +func LeftJoinInt(lx, rx []int) []int { + result := make([]int, 0, len(lx)) + rhash := hashSliceInt(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt finds and returns dissimilar data from the second int collection (right). +func RightJoinInt(lx, rx []int) []int { return LeftJoinInt(rx, lx) } + +func hashSliceInt(arr []int) map[int]struct{} { + hash := make(map[int]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinInt32Fnc func(lx, rx []int32) []int32 + +// JoinInt32 combines two int32 collections using the given join method. +func JoinInt32(larr, rarr []int32, fnc JoinInt32Fnc) []int32 { + return fnc(larr, rarr) +} + +// InnerJoinInt32 finds and returns matching data from two int32 collections. +func InnerJoinInt32(lx, rx []int32) []int32 { + result := make([]int32, 0, len(lx)+len(rx)) + rhash := hashSliceInt32(rx) + lhash := make(map[int32]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt32 finds and returns dissimilar data from two int32 collections. +func OuterJoinInt32(lx, rx []int32) []int32 { + ljoin := LeftJoinInt32(lx, rx) + rjoin := RightJoinInt32(lx, rx) + + result := make([]int32, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt32 finds and returns dissimilar data from the first int32 collection (left). +func LeftJoinInt32(lx, rx []int32) []int32 { + result := make([]int32, 0, len(lx)) + rhash := hashSliceInt32(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt32 finds and returns dissimilar data from the second int32 collection (right). +func RightJoinInt32(lx, rx []int32) []int32 { return LeftJoinInt32(rx, lx) } + +func hashSliceInt32(arr []int32) map[int32]struct{} { + hash := make(map[int32]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinInt64Fnc func(lx, rx []int64) []int64 + +// JoinInt64 combines two int64 collections using the given join method. +func JoinInt64(larr, rarr []int64, fnc JoinInt64Fnc) []int64 { + return fnc(larr, rarr) +} + +// InnerJoinInt64 finds and returns matching data from two int64 collections. +func InnerJoinInt64(lx, rx []int64) []int64 { + result := make([]int64, 0, len(lx)+len(rx)) + rhash := hashSliceInt64(rx) + lhash := make(map[int64]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinInt64 finds and returns dissimilar data from two int64 collections. +func OuterJoinInt64(lx, rx []int64) []int64 { + ljoin := LeftJoinInt64(lx, rx) + rjoin := RightJoinInt64(lx, rx) + + result := make([]int64, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinInt64 finds and returns dissimilar data from the first int64 collection (left). +func LeftJoinInt64(lx, rx []int64) []int64 { + result := make([]int64, 0, len(lx)) + rhash := hashSliceInt64(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinInt64 finds and returns dissimilar data from the second int64 collection (right). +func RightJoinInt64(lx, rx []int64) []int64 { return LeftJoinInt64(rx, lx) } + +func hashSliceInt64(arr []int64) map[int64]struct{} { + hash := make(map[int64]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinStringFnc func(lx, rx []string) []string + +// JoinString combines two string collections using the given join method. +func JoinString(larr, rarr []string, fnc JoinStringFnc) []string { + return fnc(larr, rarr) +} + +// InnerJoinString finds and returns matching data from two string collections. +func InnerJoinString(lx, rx []string) []string { + result := make([]string, 0, len(lx)+len(rx)) + rhash := hashSliceString(rx) + lhash := make(map[string]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinString finds and returns dissimilar data from two string collections. +func OuterJoinString(lx, rx []string) []string { + ljoin := LeftJoinString(lx, rx) + rjoin := RightJoinString(lx, rx) + + result := make([]string, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinString finds and returns dissimilar data from the first string collection (left). +func LeftJoinString(lx, rx []string) []string { + result := make([]string, 0, len(lx)) + rhash := hashSliceString(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinString finds and returns dissimilar data from the second string collection (right). +func RightJoinString(lx, rx []string) []string { return LeftJoinString(rx, lx) } + +func hashSliceString(arr []string) map[string]struct{} { + hash := make(map[string]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinFloat32Fnc func(lx, rx []float32) []float32 + +// JoinFloat32 combines two float32 collections using the given join method. +func JoinFloat32(larr, rarr []float32, fnc JoinFloat32Fnc) []float32 { + return fnc(larr, rarr) +} + +// InnerJoinFloat32 finds and returns matching data from two float32 collections. +func InnerJoinFloat32(lx, rx []float32) []float32 { + result := make([]float32, 0, len(lx)+len(rx)) + rhash := hashSliceFloat32(rx) + lhash := make(map[float32]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinFloat32 finds and returns dissimilar data from two float32 collections. +func OuterJoinFloat32(lx, rx []float32) []float32 { + ljoin := LeftJoinFloat32(lx, rx) + rjoin := RightJoinFloat32(lx, rx) + + result := make([]float32, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinFloat32 finds and returns dissimilar data from the first float32 collection (left). +func LeftJoinFloat32(lx, rx []float32) []float32 { + result := make([]float32, 0, len(lx)) + rhash := hashSliceFloat32(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinFloat32 finds and returns dissimilar data from the second float32 collection (right). +func RightJoinFloat32(lx, rx []float32) []float32 { return LeftJoinFloat32(rx, lx) } + +func hashSliceFloat32(arr []float32) map[float32]struct{} { + hash := make(map[float32]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} + +type JoinFloat64Fnc func(lx, rx []float64) []float64 + +// JoinFloat64 combines two float64 collections using the given join method. +func JoinFloat64(larr, rarr []float64, fnc JoinFloat64Fnc) []float64 { + return fnc(larr, rarr) +} + +// InnerJoinFloat64 finds and returns matching data from two float64 collections. +func InnerJoinFloat64(lx, rx []float64) []float64 { + result := make([]float64, 0, len(lx)+len(rx)) + rhash := hashSliceFloat64(rx) + lhash := make(map[float64]struct{}, len(lx)) + + for _, v := range lx { + _, ok := rhash[v] + _, alreadyExists := lhash[v] + if ok && !alreadyExists { + lhash[v] = struct{}{} + result = append(result, v) + } + } + return result +} + +// OuterJoinFloat64 finds and returns dissimilar data from two float64 collections. +func OuterJoinFloat64(lx, rx []float64) []float64 { + ljoin := LeftJoinFloat64(lx, rx) + rjoin := RightJoinFloat64(lx, rx) + + result := make([]float64, len(ljoin)+len(rjoin)) + for i, v := range ljoin { + result[i] = v + } + for i, v := range rjoin { + result[len(ljoin)+i] = v + } + return result +} + +// LeftJoinFloat64 finds and returns dissimilar data from the first float64 collection (left). +func LeftJoinFloat64(lx, rx []float64) []float64 { + result := make([]float64, 0, len(lx)) + rhash := hashSliceFloat64(rx) + + for _, v := range lx { + _, ok := rhash[v] + if !ok { + result = append(result, v) + } + } + return result +} + +// LeftJoinFloat64 finds and returns dissimilar data from the second float64 collection (right). +func RightJoinFloat64(lx, rx []float64) []float64 { return LeftJoinFloat64(rx, lx) } + +func hashSliceFloat64(arr []float64) map[float64]struct{} { + hash := make(map[float64]struct{}, len(arr)) + for _, i := range arr { + hash[i] = struct{}{} + } + return hash +} diff --git a/vendor/github.com/thoas/go-funk/lazy_builder.go b/vendor/github.com/thoas/go-funk/lazy_builder.go new file mode 100644 index 0000000000000..9ba1907ea108a --- /dev/null +++ b/vendor/github.com/thoas/go-funk/lazy_builder.go @@ -0,0 +1,117 @@ +package funk + +import "reflect" + +type lazyBuilder struct { + exec func() interface{} +} + +func (b *lazyBuilder) Chunk(size int) Builder { + return &lazyBuilder{func() interface{} { return Chunk(b.exec(), size) }} +} +func (b *lazyBuilder) Compact() Builder { + return &lazyBuilder{func() interface{} { return Compact(b.exec()) }} +} +func (b *lazyBuilder) Drop(n int) Builder { + return &lazyBuilder{func() interface{} { return Drop(b.exec(), n) }} +} +func (b *lazyBuilder) Filter(predicate interface{}) Builder { + return &lazyBuilder{func() interface{} { return Filter(b.exec(), predicate) }} +} +func (b *lazyBuilder) Flatten() Builder { + return &lazyBuilder{func() interface{} { return Flatten(b.exec()) }} +} +func (b *lazyBuilder) FlattenDeep() Builder { + return &lazyBuilder{func() interface{} { return FlattenDeep(b.exec()) }} +} +func (b *lazyBuilder) Initial() Builder { + return &lazyBuilder{func() interface{} { return Initial(b.exec()) }} +} +func (b *lazyBuilder) Intersect(y interface{}) Builder { + return &lazyBuilder{func() interface{} { return Intersect(b.exec(), y) }} +} +func (b *lazyBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { + return &lazyBuilder{func() interface{} { return Join(b.exec(), rarr, fnc) }} +} +func (b *lazyBuilder) Map(mapFunc interface{}) Builder { + return &lazyBuilder{func() interface{} { return Map(b.exec(), mapFunc) }} +} +func (b *lazyBuilder) FlatMap(mapFunc interface{}) Builder { + return &lazyBuilder{func() interface{} { return FlatMap(b.exec(), mapFunc) }} +} +func (b *lazyBuilder) Reverse() Builder { + return &lazyBuilder{func() interface{} { return Reverse(b.exec()) }} +} +func (b *lazyBuilder) Shuffle() Builder { + return &lazyBuilder{func() interface{} { return Shuffle(b.exec()) }} +} +func (b *lazyBuilder) Tail() Builder { + return &lazyBuilder{func() interface{} { return Tail(b.exec()) }} +} +func (b *lazyBuilder) Uniq() Builder { + return &lazyBuilder{func() interface{} { return Uniq(b.exec()) }} +} +func (b *lazyBuilder) Without(values ...interface{}) Builder { + return &lazyBuilder{func() interface{} { return Without(b.exec(), values...) }} +} + +func (b *lazyBuilder) All() bool { + return (&chainBuilder{b.exec()}).All() +} +func (b *lazyBuilder) Any() bool { + return (&chainBuilder{b.exec()}).Any() +} +func (b *lazyBuilder) Contains(elem interface{}) bool { + return Contains(b.exec(), elem) +} +func (b *lazyBuilder) Every(elements ...interface{}) bool { + return Every(b.exec(), elements...) +} +func (b *lazyBuilder) Find(predicate interface{}) interface{} { + return Find(b.exec(), predicate) +} +func (b *lazyBuilder) ForEach(predicate interface{}) { + ForEach(b.exec(), predicate) +} +func (b *lazyBuilder) ForEachRight(predicate interface{}) { + ForEachRight(b.exec(), predicate) +} +func (b *lazyBuilder) Head() interface{} { + return Head(b.exec()) +} +func (b *lazyBuilder) Keys() interface{} { + return Keys(b.exec()) +} +func (b *lazyBuilder) IndexOf(elem interface{}) int { + return IndexOf(b.exec(), elem) +} +func (b *lazyBuilder) IsEmpty() bool { + return IsEmpty(b.exec()) +} +func (b *lazyBuilder) Last() interface{} { + return Last(b.exec()) +} +func (b *lazyBuilder) LastIndexOf(elem interface{}) int { + return LastIndexOf(b.exec(), elem) +} +func (b *lazyBuilder) NotEmpty() bool { + return NotEmpty(b.exec()) +} +func (b *lazyBuilder) Product() float64 { + return Product(b.exec()) +} +func (b *lazyBuilder) Reduce(reduceFunc, acc interface{}) interface{} { + return Reduce(b.exec(), reduceFunc, acc) +} +func (b *lazyBuilder) Sum() float64 { + return Sum(b.exec()) +} +func (b *lazyBuilder) Type() reflect.Type { + return reflect.TypeOf(b.exec()) +} +func (b *lazyBuilder) Value() interface{} { + return b.exec() +} +func (b *lazyBuilder) Values() interface{} { + return Values(b.exec()) +} diff --git a/vendor/github.com/thoas/go-funk/map.go b/vendor/github.com/thoas/go-funk/map.go new file mode 100644 index 0000000000000..4dfb4f9047895 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/map.go @@ -0,0 +1,74 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// Keys creates an array of the own enumerable map keys or struct field names. +func Keys(out interface{}) interface{} { + value := redirectValue(reflect.ValueOf(out)) + valueType := value.Type() + + if value.Kind() == reflect.Map { + keys := value.MapKeys() + + length := len(keys) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) + + for i, key := range keys { + resultSlice.Index(i).Set(key) + } + + return resultSlice.Interface() + } + + if value.Kind() == reflect.Struct { + length := value.NumField() + + resultSlice := make([]string, length) + + for i := 0; i < length; i++ { + resultSlice[i] = valueType.Field(i).Name + } + + return resultSlice + } + + panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) +} + +// Values creates an array of the own enumerable map values or struct field values. +func Values(out interface{}) interface{} { + value := redirectValue(reflect.ValueOf(out)) + valueType := value.Type() + + if value.Kind() == reflect.Map { + keys := value.MapKeys() + + length := len(keys) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Elem()), length, length) + + for i, key := range keys { + resultSlice.Index(i).Set(value.MapIndex(key)) + } + + return resultSlice.Interface() + } + + if value.Kind() == reflect.Struct { + length := value.NumField() + + resultSlice := make([]interface{}, length) + + for i := 0; i < length; i++ { + resultSlice[i] = value.Field(i).Interface() + } + + return resultSlice + } + + panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) +} diff --git a/vendor/github.com/thoas/go-funk/max.go b/vendor/github.com/thoas/go-funk/max.go new file mode 100644 index 0000000000000..4b3914fcd4b68 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/max.go @@ -0,0 +1,194 @@ +package funk + +import "strings" + +// MaxInt validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int +// It returns int or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxInt(i []int) interface{} { + if len(i) == 0 { + return nil + } + var max int + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt8 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int8 +// It returns int8 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxInt8(i []int8) interface{} { + if len(i) == 0 { + return nil + } + var max int8 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt16 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int16 +// It returns int16 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxInt16(i []int16) interface{} { + if len(i) == 0 { + return nil + } + var max int16 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt32 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int32 +// It returns int32 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxInt32(i []int32) interface{} { + if len(i) == 0 { + return nil + } + var max int32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxInt64 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []int64 +// It returns int64 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxInt64(i []int64) interface{} { + if len(i) == 0 { + return nil + } + var max int64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxFloat32 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []float32 +// It returns float32 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxFloat32(i []float32) interface{} { + if len(i) == 0 { + return nil + } + var max float32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxFloat64 validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []float64 +// It returns float64 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxFloat64(i []float64) interface{} { + if len(i) == 0 { + return nil + } + var max float64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + if item > max { + max = item + } + } + return max +} + +// MaxString validates the input, compares the elements and returns the maximum element in an array/slice. +// It accepts []string +// It returns string or nil +// It returns nil for the following cases: +// - input is of length 0 +func MaxString(i []string) interface{} { + if len(i) == 0 { + return nil + } + var max string + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + max = item + continue + } + max = compareStringsMax(max, item) + } + return max +} + +// compareStrings uses the strings.Compare method to compare two strings, and returns the greater one. +func compareStringsMax(max, current string) string { + r := strings.Compare(strings.ToLower(max), strings.ToLower(current)) + if r > 0 { + return max + } + return current +} \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/min.go b/vendor/github.com/thoas/go-funk/min.go new file mode 100644 index 0000000000000..e880f7cf99874 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/min.go @@ -0,0 +1,193 @@ +package funk + +import "strings" + +// MinInt validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int +// It returns int or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinInt(i []int) interface{} { + if len(i) == 0 { + return nil + } + var min int + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt8 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int8 +// It returns int8 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinInt8(i []int8) interface{} { + if len(i) == 0 { + return nil + } + var min int8 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt16 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int16 +// It returns int16 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinInt16(i []int16) interface{} { + if len(i) == 0 { + return nil + } + var min int16 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt32 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int32 +// It returns int32 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinInt32(i []int32) interface{} { + if len(i) == 0 { + return nil + } + var min int32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinInt64 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []int64 +// It returns int64 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinInt64(i []int64) interface{} { + if len(i) == 0 { + return nil + } + var min int64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinFloat32 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []float32 +// It returns float32 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinFloat32(i []float32) interface{} { + if len(i) == 0 { + return nil + } + var min float32 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinFloat64 validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []float64 +// It returns float64 or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinFloat64(i []float64) interface{} { + if len(i) == 0 { + return nil + } + var min float64 + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + if item < min { + min = item + } + } + return min +} + +// MinString validates the input, compares the elements and returns the minimum element in an array/slice. +// It accepts []string +// It returns string or nil +// It returns nil for the following cases: +// - input is of length 0 +func MinString(i []string) interface{} { + if len(i) == 0 { + return nil + } + var min string + for idx := 0; idx < len(i); idx++ { + item := i[idx] + if idx == 0 { + min = item + continue + } + min = compareStringsMin(min, item) + } + return min +} + +func compareStringsMin(min, current string) string { + r := strings.Compare(strings.ToLower(min), strings.ToLower(current)) + if r < 0 { + return min + } + return current +} \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/operation.go b/vendor/github.com/thoas/go-funk/operation.go new file mode 100644 index 0000000000000..170948b4a6ca9 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/operation.go @@ -0,0 +1,69 @@ +package funk + +import ( + "fmt" + "reflect" +) + +func calculate(arr interface{}, name string, operation rune) float64 { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length == 0 { + return 0 + } + + result := map[rune]float64{ + '+': 0.0, + '*': 1, + }[operation] + + for i := 0; i < length; i++ { + elem := redirectValue(value.Index(i)).Interface() + + var value float64 + switch e := elem.(type) { + case int: + value = float64(e) + case int8: + value = float64(e) + case int16: + value = float64(e) + case int32: + value = float64(e) + case int64: + value = float64(e) + case float32: + value = float64(e) + case float64: + value = e + } + + switch operation { + case '+': + result += value + case '*': + result *= value + } + } + + return result + } + + panic(fmt.Sprintf("Type %s is not supported by %s", valueType.String(), name)) +} + +// Sum computes the sum of the values in array. +func Sum(arr interface{}) float64 { + return calculate(arr, "Sum", '+') +} + +// Product computes the product of the values in array. +func Product(arr interface{}) float64 { + return calculate(arr, "Product", '*') +} diff --git a/vendor/github.com/thoas/go-funk/permutation.go b/vendor/github.com/thoas/go-funk/permutation.go new file mode 100644 index 0000000000000..0b109930625cb --- /dev/null +++ b/vendor/github.com/thoas/go-funk/permutation.go @@ -0,0 +1,29 @@ +package funk + +import "errors" + +// NextPermutation Implement next permutation, +// which rearranges numbers into the lexicographically next greater permutation of numbers. +func NextPermutation(nums []int) error { + n := len(nums) + if n == 0 { + return errors.New("nums is empty") + } + + i := n - 2 + + for i >= 0 && nums[i] >= nums[i+1] { + i-- + } + + if i >= 0 { + j := n - 1 + for j >= 0 && nums[i] >= nums[j] { + j-- + } + nums[i], nums[j] = nums[j], nums[i] + } + + ReverseInt(nums[i+1:]) + return nil +} diff --git a/vendor/github.com/thoas/go-funk/predicate.go b/vendor/github.com/thoas/go-funk/predicate.go new file mode 100644 index 0000000000000..e6b4131de2e6c --- /dev/null +++ b/vendor/github.com/thoas/go-funk/predicate.go @@ -0,0 +1,47 @@ +package funk + +import ( + "reflect" +) + +// predicatesImpl contains the common implementation of AnyPredicates and AllPredicates. +func predicatesImpl(value interface{}, wantedAnswer bool, predicates interface{}) bool { + if !IsCollection(predicates) { + panic("Predicates parameter must be an iteratee") + } + + predicatesValue := reflect.ValueOf(predicates) + inputValue := reflect.ValueOf(value) + + for i := 0; i < predicatesValue.Len(); i++ { + funcValue := predicatesValue.Index(i) + if !IsFunction(funcValue.Interface()) { + panic("Got non function as predicate") + } + + funcType := funcValue.Type() + if !IsPredicate(funcValue.Interface()) { + panic("Predicate function must have 1 parameter and must return boolean") + } + + if !inputValue.Type().ConvertibleTo(funcType.In(0)) { + panic("Given value is not compatible with type of parameter for the predicate.") + } + if result := funcValue.Call([]reflect.Value{inputValue}); wantedAnswer == result[0].Bool() { + return wantedAnswer + } + } + + return !wantedAnswer +} + +// AnyPredicates gets a value and a series of predicates, and return true if at least one of the predicates +// is true. +func AnyPredicates(value interface{}, predicates interface{}) bool { + return predicatesImpl(value, true, predicates) +} + +// AllPredicates gets a value and a series of predicates, and return true if all of the predicates are true. +func AllPredicates(value interface{}, predicates interface{}) bool { + return predicatesImpl(value, false, predicates) +} diff --git a/vendor/github.com/thoas/go-funk/presence.go b/vendor/github.com/thoas/go-funk/presence.go new file mode 100644 index 0000000000000..9ab0f0f42703a --- /dev/null +++ b/vendor/github.com/thoas/go-funk/presence.go @@ -0,0 +1,207 @@ +package funk + +import ( + "fmt" + "reflect" + "strings" +) + +// Filter iterates over elements of collection, returning an array of +// all elements predicate returns truthy for. +func Filter(arr interface{}, predicate interface{}) interface{} { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(predicate, 1, 1) { + panic("Second argument must be function") + } + + funcValue := reflect.ValueOf(predicate) + + funcType := funcValue.Type() + + if funcType.Out(0).Kind() != reflect.Bool { + panic("Return argument should be a boolean") + } + + arrValue := reflect.ValueOf(arr) + + arrType := arrValue.Type() + + // Get slice type corresponding to array type + resultSliceType := reflect.SliceOf(arrType.Elem()) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arrValue.Len(); i++ { + elem := arrValue.Index(i) + + result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) + + if result { + resultSlice = reflect.Append(resultSlice, elem) + } + } + + return resultSlice.Interface() +} + +// Find iterates over elements of collection, returning the first +// element predicate returns truthy for. +func Find(arr interface{}, predicate interface{}) interface{} { + _, val := FindKey(arr, predicate) + return val +} + +// Find iterates over elements of collection, returning the first +// element of an array and random of a map which predicate returns truthy for. +func FindKey(arr interface{}, predicate interface{}) (matchKey, matchEle interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(predicate, 1, 1) { + panic("Second argument must be function") + } + + funcValue := reflect.ValueOf(predicate) + + funcType := funcValue.Type() + + if funcType.Out(0).Kind() != reflect.Bool { + panic("Return argument should be a boolean") + } + + arrValue := reflect.ValueOf(arr) + var keyArrs []reflect.Value + + isMap := arrValue.Kind() == reflect.Map + if isMap { + keyArrs = arrValue.MapKeys() + } + for i := 0; i < arrValue.Len(); i++ { + var ( + elem reflect.Value + key reflect.Value + ) + if isMap { + key = keyArrs[i] + elem = arrValue.MapIndex(key) + } else { + key = reflect.ValueOf(i) + elem = arrValue.Index(i) + } + + result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) + + if result { + return key.Interface(), elem.Interface() + } + } + + return nil, nil +} + +// IndexOf gets the index at which the first occurrence of value is found in array or return -1 +// if the value cannot be found +func IndexOf(in interface{}, elem interface{}) int { + inValue := reflect.ValueOf(in) + + elemValue := reflect.ValueOf(elem) + + inType := inValue.Type() + + if inType.Kind() == reflect.String { + return strings.Index(inValue.String(), elemValue.String()) + } + + if inType.Kind() == reflect.Slice { + equalTo := equal(elem) + for i := 0; i < inValue.Len(); i++ { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return i + } + } + } + + return -1 +} + +// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1 +// if the value cannot be found +func LastIndexOf(in interface{}, elem interface{}) int { + inValue := reflect.ValueOf(in) + + elemValue := reflect.ValueOf(elem) + + inType := inValue.Type() + + if inType.Kind() == reflect.String { + return strings.LastIndex(inValue.String(), elemValue.String()) + } + + if inType.Kind() == reflect.Slice { + length := inValue.Len() + + equalTo := equal(elem) + for i := length - 1; i >= 0; i-- { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return i + } + } + } + + return -1 +} + +// Contains returns true if an element is present in a iteratee. +func Contains(in interface{}, elem interface{}) bool { + inValue := reflect.ValueOf(in) + elemValue := reflect.ValueOf(elem) + inType := inValue.Type() + + switch inType.Kind() { + case reflect.String: + return strings.Contains(inValue.String(), elemValue.String()) + case reflect.Map: + equalTo := equal(elem, true) + for _, key := range inValue.MapKeys() { + if equalTo(key, inValue.MapIndex(key)) { + return true + } + } + case reflect.Slice, reflect.Array: + equalTo := equal(elem) + for i := 0; i < inValue.Len(); i++ { + if equalTo(reflect.Value{}, inValue.Index(i)) { + return true + } + } + default: + panic(fmt.Sprintf("Type %s is not supported by Contains, supported types are String, Map, Slice, Array", inType.String())) + } + + return false +} + +// Every returns true if every element is present in a iteratee. +func Every(in interface{}, elements ...interface{}) bool { + for _, elem := range elements { + if !Contains(in, elem) { + return false + } + } + return true +} + +// Some returns true if atleast one element is present in an iteratee. +func Some(in interface{}, elements ...interface{}) bool { + for _, elem := range elements { + if Contains(in, elem) { + return true + } + } + return false +} diff --git a/vendor/github.com/thoas/go-funk/reduce.go b/vendor/github.com/thoas/go-funk/reduce.go new file mode 100644 index 0000000000000..579b82254d7ae --- /dev/null +++ b/vendor/github.com/thoas/go-funk/reduce.go @@ -0,0 +1,87 @@ +package funk + +import ( + "reflect" +) + +// Reduce takes a collection and reduces it to a single value using a reduction +// function (or a valid symbol) and an accumulator value. +func Reduce(arr, reduceFunc, acc interface{}) interface{} { + arrValue := redirectValue(reflect.ValueOf(arr)) + + if !IsIteratee(arrValue.Interface()) { + panic("First parameter must be an iteratee") + } + + returnType := reflect.TypeOf(Reduce).Out(0) + + isFunc := IsFunction(reduceFunc, 2, 1) + isRune := reflect.TypeOf(reduceFunc).Kind() == reflect.Int32 + + if !(isFunc || isRune) { + panic("Second argument must be a valid function or rune") + } + + accValue := reflect.ValueOf(acc) + sliceElemType := sliceElem(arrValue.Type()) + + if isRune { + if arrValue.Kind() == reflect.Slice && sliceElemType.Kind() == reflect.Interface { + accValue = accValue.Convert(returnType) + } else { + accValue = accValue.Convert(sliceElemType) + } + } else { + accValue = accValue.Convert(reflect.TypeOf(reduceFunc).In(0)) + } + + accType := accValue.Type() + + // Generate reduce function if was passed as rune + if isRune { + reduceSign := reduceFunc.(int32) + + if ok := map[rune]bool{'+': true, '*': true}[reduceSign]; !ok { + panic("Invalid reduce sign, allowed: '+' and '*'") + } + + in := []reflect.Type{accType, sliceElemType} + out := []reflect.Type{accType} + funcType := reflect.FuncOf(in, out, false) + + reduceFunc = reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value { + acc := args[0].Interface() + elem := args[1].Interface() + + var result float64 + params := []interface{}{acc, elem} + switch reduceSign { + case '+': + result = Sum(params) + case '*': + result = Product(params) + } + + return []reflect.Value{reflect.ValueOf(result).Convert(accType)} + }).Interface() + } + + funcValue := reflect.ValueOf(reduceFunc) + funcType := funcValue.Type() + + for i := 0; i < arrValue.Len(); i++ { + if accType.ConvertibleTo(funcType.In(0)) { + accValue = accValue.Convert(funcType.In(0)) + } + + arrElementValue := arrValue.Index(i) + if sliceElemType.ConvertibleTo(funcType.In(1)) { + arrElementValue = arrElementValue.Convert(funcType.In(1)) + } + + result := funcValue.Call([]reflect.Value{accValue, arrElementValue}) + accValue = result[0] + } + + return accValue.Convert(returnType).Interface() +} diff --git a/vendor/github.com/thoas/go-funk/retrieve.go b/vendor/github.com/thoas/go-funk/retrieve.go new file mode 100644 index 0000000000000..5a358fe0a657f --- /dev/null +++ b/vendor/github.com/thoas/go-funk/retrieve.go @@ -0,0 +1,93 @@ +package funk + +import ( + "reflect" + "strings" +) + +// Get retrieves the value at path of struct(s). +func Get(out interface{}, path string) interface{} { + result := get(reflect.ValueOf(out), path) + + if result.Kind() != reflect.Invalid && !result.IsZero() { + return result.Interface() + } + + return nil +} + +func get(value reflect.Value, path string) reflect.Value { + if value.Kind() == reflect.Slice || value.Kind() == reflect.Array { + var resultSlice reflect.Value + + length := value.Len() + + if length == 0 { + zeroElement := reflect.Zero(value.Type().Elem()) + pathValue := get(zeroElement, path) + value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0) + + return value + } + + for i := 0; i < length; i++ { + item := value.Index(i) + + resultValue := get(item, path) + + if resultValue.Kind() == reflect.Invalid || resultValue.IsZero() { + continue + } + + resultType := resultValue.Type() + + if resultSlice.Kind() == reflect.Invalid { + resultType := reflect.SliceOf(resultType) + + resultSlice = reflect.MakeSlice(resultType, 0, 0) + } + + resultSlice = reflect.Append(resultSlice, resultValue) + } + + // if the result is a slice of a slice, we need to flatten it + if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice { + return flattenDeep(resultSlice) + } + + return resultSlice + } + + parts := strings.Split(path, ".") + + for _, part := range parts { + value = redirectValue(value) + kind := value.Kind() + + switch kind { + case reflect.Invalid: + continue + case reflect.Struct: + value = value.FieldByName(part) + case reflect.Map: + value = value.MapIndex(reflect.ValueOf(part)) + case reflect.Slice, reflect.Array: + value = get(value, part) + default: + return reflect.ValueOf(nil) + } + } + + return value +} + +// Get retrieves the value of the pointer or default. +func GetOrElse(v interface{}, def interface{}) interface{} { + val := reflect.ValueOf(v) + if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) { + return def + } else if val.Kind() != reflect.Ptr { + return v + } + return val.Elem().Interface() +} diff --git a/vendor/github.com/thoas/go-funk/scan.go b/vendor/github.com/thoas/go-funk/scan.go new file mode 100644 index 0000000000000..d0d1d454f6518 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/scan.go @@ -0,0 +1,192 @@ +package funk + +import ( + "fmt" + "reflect" +) + +// ForEach iterates over elements of collection and invokes iteratee +// for each element. +func ForEach(arr interface{}, predicate interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + var ( + funcValue = reflect.ValueOf(predicate) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + funcType = funcValue.Type() + ) + + if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { + if !IsFunction(predicate, 1, 0) { + panic("Second argument must be a function with one parameter") + } + + arrElemType := arrValue.Type().Elem() + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) { + panic("Map function's argument is not compatible with type of array.") + } + + for i := 0; i < arrValue.Len(); i++ { + funcValue.Call([]reflect.Value{arrValue.Index(i)}) + } + } + + if arrType.Kind() == reflect.Map { + if !IsFunction(predicate, 2, 0) { + panic("Second argument must be a function with two parameters") + } + + // Type checking for Map = (key, value) + keyType := arrType.Key() + valueType := arrType.Elem() + + if !keyType.ConvertibleTo(funcType.In(0)) { + panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) + } + + if !valueType.ConvertibleTo(funcType.In(1)) { + panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) + } + + for _, key := range arrValue.MapKeys() { + funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + } + } +} + +// ForEachRight iterates over elements of collection from the right and invokes iteratee +// for each element. +func ForEachRight(arr interface{}, predicate interface{}) { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + var ( + funcValue = reflect.ValueOf(predicate) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + funcType = funcValue.Type() + ) + + if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { + if !IsFunction(predicate, 1, 0) { + panic("Second argument must be a function with one parameter") + } + + arrElemType := arrValue.Type().Elem() + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) { + panic("Map function's argument is not compatible with type of array.") + } + + for i := arrValue.Len() - 1; i >= 0; i-- { + funcValue.Call([]reflect.Value{arrValue.Index(i)}) + } + } + + if arrType.Kind() == reflect.Map { + if !IsFunction(predicate, 2, 0) { + panic("Second argument must be a function with two parameters") + } + + // Type checking for Map = (key, value) + keyType := arrType.Key() + valueType := arrType.Elem() + + if !keyType.ConvertibleTo(funcType.In(0)) { + panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) + } + + if !valueType.ConvertibleTo(funcType.In(1)) { + panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) + } + + keys := Reverse(arrValue.MapKeys()).([]reflect.Value) + + for _, key := range keys { + funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + } + } +} + +// Head gets the first element of array. +func Head(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + if value.Len() == 0 { + return nil + } + + return value.Index(0).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Head", valueType.String())) +} + +// Last gets the last element of array. +func Last(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + if value.Len() == 0 { + return nil + } + + return value.Index(value.Len() - 1).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Last", valueType.String())) +} + +// Initial gets all but the last element of array. +func Initial(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length <= 1 { + return arr + } + + return value.Slice(0, length-1).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) +} + +// Tail gets all but the first element of array. +func Tail(arr interface{}) interface{} { + value := redirectValue(reflect.ValueOf(arr)) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + if length <= 1 { + return arr + } + + return value.Slice(1, length).Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) +} diff --git a/vendor/github.com/thoas/go-funk/short_if.go b/vendor/github.com/thoas/go-funk/short_if.go new file mode 100644 index 0000000000000..347c03b6205fa --- /dev/null +++ b/vendor/github.com/thoas/go-funk/short_if.go @@ -0,0 +1,8 @@ +package funk + +func ShortIf(condition bool, a interface{}, b interface{}) interface{} { + if condition { + return a + } + return b +} diff --git a/vendor/github.com/thoas/go-funk/subset.go b/vendor/github.com/thoas/go-funk/subset.go new file mode 100644 index 0000000000000..de47be1517621 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/subset.go @@ -0,0 +1,41 @@ +package funk + +import ( + "reflect" +) + +// Subset returns true if collection x is a subset of y. +func Subset(x interface{}, y interface{}) bool { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + if xValue.Len() == 0 { + return true + } + + if yValue.Len() == 0 || yValue.Len() < xValue.Len() { + return false + } + + for i := 0; i < xValue.Len(); i++ { + if !Contains(yValue.Interface(), xValue.Index(i).Interface()) { + return false + } + } + + return true +} diff --git a/vendor/github.com/thoas/go-funk/subtraction.go b/vendor/github.com/thoas/go-funk/subtraction.go new file mode 100644 index 0000000000000..10a0a982021e8 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/subtraction.go @@ -0,0 +1,87 @@ +package funk + +import ( + "reflect" +) + +// Subtract returns the subtraction between two collections. +func Subtract(x interface{}, y interface{}) interface{} { + if !IsCollection(x) { + panic("First parameter must be a collection") + } + if !IsCollection(y) { + panic("Second parameter must be a collection") + } + + hash := map[interface{}]struct{}{} + + xValue := reflect.ValueOf(x) + xType := xValue.Type() + + yValue := reflect.ValueOf(y) + yType := yValue.Type() + + if NotEqual(xType, yType) { + panic("Parameters must have the same type") + } + + zType := reflect.SliceOf(xType.Elem()) + zSlice := reflect.MakeSlice(zType, 0, 0) + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + hash[v] = struct{}{} + } + + for i := 0; i < yValue.Len(); i++ { + v := yValue.Index(i).Interface() + _, ok := hash[v] + if ok { + delete(hash, v) + } + } + + for i := 0; i < xValue.Len(); i++ { + v := xValue.Index(i).Interface() + _, ok := hash[v] + if ok { + zSlice = reflect.Append(zSlice, xValue.Index(i)) + } + } + + return zSlice.Interface() +} + +// SubtractString returns the subtraction between two collections of string +func SubtractString(x []string, y []string) []string { + if len(x) == 0 { + return []string{} + } + + if len(y) == 0 { + return x + } + + slice := []string{} + hash := map[string]struct{}{} + + for _, v := range x { + hash[v] = struct{}{} + } + + for _, v := range y { + _, ok := hash[v] + if ok { + delete(hash, v) + } + } + + for _, v := range x { + _, ok := hash[v] + if ok { + slice = append(slice, v) + } + } + + return slice +} diff --git a/vendor/github.com/thoas/go-funk/transform.go b/vendor/github.com/thoas/go-funk/transform.go new file mode 100644 index 0000000000000..e4fd65bb26b98 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/transform.go @@ -0,0 +1,555 @@ +package funk + +import ( + "fmt" + "math/rand" + "reflect" + "strings" +) + +// Chunk creates an array of elements split into groups with the length of size. +// If array can't be split evenly, the final chunk will be +// the remaining element. +func Chunk(arr interface{}, size int) interface{} { + if !IsIteratee(arr) { + panic("First parameter must be neither array nor slice") + } + + if size == 0 { + return arr + } + + arrValue := reflect.ValueOf(arr) + + arrType := arrValue.Type() + + resultSliceType := reflect.SliceOf(arrType) + + // Initialize final result slice which will contains slice + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + itemType := arrType.Elem() + + var itemSlice reflect.Value + + itemSliceType := reflect.SliceOf(itemType) + + length := arrValue.Len() + + for i := 0; i < length; i++ { + if i%size == 0 || i == 0 { + if itemSlice.Kind() != reflect.Invalid { + resultSlice = reflect.Append(resultSlice, itemSlice) + } + + itemSlice = reflect.MakeSlice(itemSliceType, 0, 0) + } + + itemSlice = reflect.Append(itemSlice, arrValue.Index(i)) + + if i == length-1 { + resultSlice = reflect.Append(resultSlice, itemSlice) + } + } + + return resultSlice.Interface() +} + +// ToMap transforms a slice of instances to a Map. +// []*Foo => Map +func ToMap(in interface{}, pivot string) interface{} { + value := reflect.ValueOf(in) + + // input value must be a slice + if value.Kind() != reflect.Slice { + panic(fmt.Sprintf("%v must be a slice", in)) + } + + inType := value.Type() + + structType := inType.Elem() + + // retrieve the struct in the slice to deduce key type + if structType.Kind() == reflect.Ptr { + structType = structType.Elem() + } + + field, _ := structType.FieldByName(pivot) + + // value of the map will be the input type + collectionType := reflect.MapOf(field.Type, inType.Elem()) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for i := 0; i < value.Len(); i++ { + instance := value.Index(i) + var field reflect.Value + + if instance.Kind() == reflect.Ptr { + field = instance.Elem().FieldByName(pivot) + } else { + field = instance.FieldByName(pivot) + } + + collection.SetMapIndex(field, instance) + } + + return collection.Interface() +} + +func mapSlice(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { + funcType := funcValue.Type() + + if funcType.NumIn() != 1 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { + panic("Map function with an array must have one parameter and must return one or two parameters") + } + + arrElemType := arrValue.Type().Elem() + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) { + panic("Map function's argument is not compatible with type of array.") + } + + if funcType.NumOut() == 1 { + // Get slice type corresponding to function's return value's type. + resultSliceType := reflect.SliceOf(funcType.Out(0)) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for i := 0; i < arrValue.Len(); i++ { + result := funcValue.Call([]reflect.Value{arrValue.Index(i)})[0] + + resultSlice = reflect.Append(resultSlice, result) + } + + return resultSlice + } + + if funcType.NumOut() == 2 { + // value of the map will be the input type + collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for i := 0; i < arrValue.Len(); i++ { + results := funcValue.Call([]reflect.Value{arrValue.Index(i)}) + + collection.SetMapIndex(results[0], results[1]) + } + + return collection + } + + return reflect.Value{} +} + +func mapMap(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { + funcType := funcValue.Type() + + if funcType.NumIn() != 2 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { + panic("Map function with a map must have two parameters and must return one or two parameters") + } + + // Only one returned parameter, should be a slice + if funcType.NumOut() == 1 { + // Get slice type corresponding to function's return value's type. + resultSliceType := reflect.SliceOf(funcType.Out(0)) + + // MakeSlice takes a slice kind type, and makes a slice. + resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) + + for _, key := range arrValue.MapKeys() { + results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + + result := results[0] + + resultSlice = reflect.Append(resultSlice, result) + } + + return resultSlice + } + + // two parameters, should be a map + if funcType.NumOut() == 2 { + // value of the map will be the input type + collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) + + // create a map from scratch + collection := reflect.MakeMap(collectionType) + + for _, key := range arrValue.MapKeys() { + results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + + collection.SetMapIndex(results[0], results[1]) + + } + + return collection + } + + return reflect.Value{} +} + +// Map manipulates an iteratee and transforms it to another type. +func Map(arr interface{}, mapFunc interface{}) interface{} { + result := mapFn(arr, mapFunc, "Map") + + if result.IsValid() { + return result.Interface() + } + + return nil +} + +func mapFn(arr interface{}, mapFunc interface{}, funcName string) reflect.Value { + if !IsIteratee(arr) { + panic("First parameter must be an iteratee") + } + + if !IsFunction(mapFunc) { + panic("Second argument must be function") + } + + var ( + funcValue = reflect.ValueOf(mapFunc) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + ) + + kind := arrType.Kind() + + if kind == reflect.Slice || kind == reflect.Array { + return mapSlice(arrValue, funcValue) + } else if kind == reflect.Map { + return mapMap(arrValue, funcValue) + } + + panic(fmt.Sprintf("Type %s is not supported by "+funcName, arrType.String())) +} + +// FlatMap manipulates an iteratee and transforms it to a flattened collection of another type. +func FlatMap(arr interface{}, mapFunc interface{}) interface{} { + result := mapFn(arr, mapFunc, "FlatMap") + + if result.IsValid() { + return flatten(result).Interface() + } + + return nil +} + +// Flatten flattens a two-dimensional array. +func Flatten(out interface{}) interface{} { + return flatten(reflect.ValueOf(out)).Interface() +} + +func flatten(value reflect.Value) reflect.Value { + sliceType := value.Type() + + if (value.Kind() != reflect.Slice && value.Kind() != reflect.Array) || + (sliceType.Elem().Kind() != reflect.Slice && sliceType.Elem().Kind() != reflect.Array) { + panic("Argument must be an array or slice of at least two dimensions") + } + + resultSliceType := sliceType.Elem().Elem() + + resultSlice := reflect.MakeSlice(reflect.SliceOf(resultSliceType), 0, 0) + + length := value.Len() + + for i := 0; i < length; i++ { + item := value.Index(i) + + resultSlice = reflect.AppendSlice(resultSlice, item) + } + + return resultSlice +} + +// FlattenDeep recursively flattens array. +func FlattenDeep(out interface{}) interface{} { + return flattenDeep(reflect.ValueOf(out)).Interface() +} + +func flattenDeep(value reflect.Value) reflect.Value { + sliceType := sliceElem(value.Type()) + + resultSlice := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, 0) + + return flattenRecursive(value, resultSlice) +} + +func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { + length := value.Len() + + for i := 0; i < length; i++ { + item := value.Index(i) + kind := item.Kind() + + if kind == reflect.Slice || kind == reflect.Array { + result = flattenRecursive(item, result) + } else { + result = reflect.Append(result, item) + } + } + + return result +} + +// Shuffle creates an array of shuffled values +func Shuffle(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length) + + for i, v := range rand.Perm(length) { + resultSlice.Index(i).Set(value.Index(v)) + } + + return resultSlice.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Shuffle", valueType.String())) +} + +// Reverse transforms an array the first element will become the last, +// the second element will become the second to last, etc. +func Reverse(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.String { + return ReverseString(in.(string)) + } + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length) + + j := 0 + for i := length - 1; i >= 0; i-- { + resultSlice.Index(j).Set(value.Index(i)) + j++ + } + + return resultSlice.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Reverse", valueType.String())) +} + +// Uniq creates an array with unique values. +func Uniq(in interface{}) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + result := makeSlice(value, 0) + + seen := make(map[interface{}]bool, length) + j := 0 + + for i := 0; i < length; i++ { + val := value.Index(i) + v := val.Interface() + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = true + result = reflect.Append(result, val) + j++ + } + + return result.Interface() + } + + panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) +} + +// ConvertSlice converts a slice type to another, +// a perfect example would be to convert a slice of struct to a slice of interface. +func ConvertSlice(in interface{}, out interface{}) { + srcValue := reflect.ValueOf(in) + + dstValue := reflect.ValueOf(out) + + if dstValue.Kind() != reflect.Ptr { + panic("Second argument must be a pointer") + } + + dstValue = dstValue.Elem() + + if srcValue.Kind() != reflect.Slice && srcValue.Kind() != reflect.Array { + panic("First argument must be an array or slice") + } + + if dstValue.Kind() != reflect.Slice && dstValue.Kind() != reflect.Array { + panic("Second argument must be an array or slice") + } + + // returns value that points to dstValue + direct := reflect.Indirect(dstValue) + + length := srcValue.Len() + + for i := 0; i < length; i++ { + dstValue = reflect.Append(dstValue, srcValue.Index(i)) + } + + direct.Set(dstValue) +} + +// Drop creates an array/slice with `n` elements dropped from the beginning. +func Drop(in interface{}, n int) interface{} { + value := reflect.ValueOf(in) + valueType := value.Type() + + kind := value.Kind() + + if kind == reflect.Array || kind == reflect.Slice { + length := value.Len() + + resultSlice := makeSlice(value, length-n) + + j := 0 + for i := n; i < length; i++ { + resultSlice.Index(j).Set(value.Index(i)) + j++ + } + + return resultSlice.Interface() + + } + + panic(fmt.Sprintf("Type %s is not supported by Drop", valueType.String())) +} + +// Prune returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field name. +// For lookup paths by field tag instead, use funk.PruneByTag() +func Prune(in interface{}, paths []string) (interface{}, error) { + return pruneByTag(in, paths, nil /*tag*/) +} + +// pruneByTag returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field Tag "tag". +func PruneByTag(in interface{}, paths []string, tag string) (interface{}, error) { + return pruneByTag(in, paths, &tag) +} + +// pruneByTag returns a copy of "in" that only contains fields in "paths" +// which are looked up using struct field Tag "tag". If tag is nil, +// traverse paths using struct field name +func pruneByTag(in interface{}, paths []string, tag *string) (interface{}, error) { + + inValue := reflect.ValueOf(in) + + ret := reflect.New(inValue.Type()).Elem() + + for _, path := range paths { + parts := strings.Split(path, ".") + if err := prune(inValue, ret, parts, tag); err != nil { + return nil, err + } + } + return ret.Interface(), nil +} + +func prune(inValue reflect.Value, ret reflect.Value, parts []string, tag *string) error { + + if len(parts) == 0 { + // we reached the location that ret needs to hold inValue + // Note: The value at the end of the path is not copied, maybe we need to change. + // ret and the original data holds the same reference to this value + ret.Set(inValue) + return nil + } + + inKind := inValue.Kind() + + switch inKind { + case reflect.Ptr: + if inValue.IsNil() { + // TODO validate + return nil + } + if ret.IsNil() { + // init ret and go to next level + ret.Set(reflect.New(inValue.Type().Elem())) + } + return prune(inValue.Elem(), ret.Elem(), parts, tag) + case reflect.Struct: + part := parts[0] + var fValue reflect.Value + var fRet reflect.Value + if tag == nil { + // use field name + fValue = inValue.FieldByName(part) + if !fValue.IsValid() { + return fmt.Errorf("field name %v is not found in struct %v", part, inValue.Type().String()) + } + fRet = ret.FieldByName(part) + } else { + // search tag that has key equal to part + found := false + for i := 0; i < inValue.NumField(); i++ { + f := inValue.Type().Field(i) + if key, ok := f.Tag.Lookup(*tag); ok { + if key == part { + fValue = inValue.Field(i) + fRet = ret.Field(i) + found = true + break + } + } + } + if !found { + return fmt.Errorf("Struct tag %v is not found with key %v", *tag, part) + } + } + // init Ret is zero and go down one more level + if fRet.IsZero() { + fRet.Set(reflect.New(fValue.Type()).Elem()) + } + return prune(fValue, fRet, parts[1:], tag) + case reflect.Array, reflect.Slice: + // set all its elements + length := inValue.Len() + // init ret + if ret.IsZero() { + if inKind == reflect.Slice { + ret.Set(reflect.MakeSlice(inValue.Type(), length /*len*/, length /*cap*/)) + } else { // array + ret.Set(reflect.New(inValue.Type()).Elem()) + } + } + for j := 0; j < length; j++ { + if err := prune(inValue.Index(j), ret.Index(j), parts, tag); err != nil { + return err + } + } + default: + return fmt.Errorf("path %v cannot be looked up on kind of %v", strings.Join(parts, "."), inValue.Kind()) + } + + return nil +} diff --git a/vendor/github.com/thoas/go-funk/typesafe.go b/vendor/github.com/thoas/go-funk/typesafe.go new file mode 100644 index 0000000000000..3242dc02dc0c9 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/typesafe.go @@ -0,0 +1,967 @@ +package funk + +import ( + "math/rand" +) + +// InInts is an alias of ContainsInt, returns true if an int is present in a iteratee. +func InInts(s []int, v int) bool { + return ContainsInt(s, v) +} + +// InInt32s is an alias of ContainsInt32, returns true if an int32 is present in a iteratee. +func InInt32s(s []int32, v int32) bool { + return ContainsInt32(s, v) +} + +// InInt64s is an alias of ContainsInt64, returns true if an int64 is present in a iteratee. +func InInt64s(s []int64, v int64) bool { + return ContainsInt64(s, v) +} + +// InUInts is an alias of ContainsUInt, returns true if an uint is present in a iteratee. +func InUInts(s []uint, v uint) bool { + return ContainsUInt(s, v) +} + +// InUInt32s is an alias of ContainsUInt32, returns true if an uint32 is present in a iteratee. +func InUInt32s(s []uint32, v uint32) bool { + return ContainsUInt32(s, v) +} + +// InUInt64s is an alias of ContainsUInt64, returns true if an uint64 is present in a iteratee. +func InUInt64s(s []uint64, v uint64) bool { + return ContainsUInt64(s, v) +} + +// InStrings is an alias of ContainsString, returns true if a string is present in a iteratee. +func InStrings(s []string, v string) bool { + return ContainsString(s, v) +} + +// InFloat32s is an alias of ContainsFloat32, returns true if a float32 is present in a iteratee. +func InFloat32s(s []float32, v float32) bool { + return ContainsFloat32(s, v) +} + +// InFloat64s is an alias of ContainsFloat64, returns true if a float64 is present in a iteratee. +func InFloat64s(s []float64, v float64) bool { + return ContainsFloat64(s, v) +} + +// FindFloat64 iterates over a collection of float64, returning an array of +// all float64 elements predicate returns truthy for. +func FindFloat64(s []float64, cb func(s float64) bool) (float64, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0.0, false +} + +// FindFloat32 iterates over a collection of float32, returning the first +// float32 element predicate returns truthy for. +func FindFloat32(s []float32, cb func(s float32) bool) (float32, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0.0, false +} + +// FindInt iterates over a collection of int, returning the first +// int element predicate returns truthy for. +func FindInt(s []int, cb func(s int) bool) (int, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindInt32 iterates over a collection of int32, returning the first +// int32 element predicate returns truthy for. +func FindInt32(s []int32, cb func(s int32) bool) (int32, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindInt64 iterates over a collection of int64, returning the first +// int64 element predicate returns truthy for. +func FindInt64(s []int64, cb func(s int64) bool) (int64, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return 0, false +} + +// FindString iterates over a collection of string, returning the first +// string element predicate returns truthy for. +func FindString(s []string, cb func(s string) bool) (string, bool) { + for _, i := range s { + result := cb(i) + + if result { + return i, true + } + } + + return "", false +} + +// FilterFloat64 iterates over a collection of float64, returning an array of +// all float64 elements predicate returns truthy for. +func FilterFloat64(s []float64, cb func(s float64) bool) []float64 { + results := []float64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterFloat32 iterates over a collection of float32, returning an array of +// all float32 elements predicate returns truthy for. +func FilterFloat32(s []float32, cb func(s float32) bool) []float32 { + results := []float32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt iterates over a collection of int, returning an array of +// all int elements predicate returns truthy for. +func FilterInt(s []int, cb func(s int) bool) []int { + results := []int{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt32 iterates over a collection of int32, returning an array of +// all int32 elements predicate returns truthy for. +func FilterInt32(s []int32, cb func(s int32) bool) []int32 { + results := []int32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterInt64 iterates over a collection of int64, returning an array of +// all int64 elements predicate returns truthy for. +func FilterInt64(s []int64, cb func(s int64) bool) []int64 { + results := []int64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt iterates over a collection of uint, returning an array of +// all uint elements predicate returns truthy for. +func FilterUInt(s []uint, cb func(s uint) bool) []uint { + results := []uint{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt32 iterates over a collection of uint32, returning an array of +// all uint32 elements predicate returns truthy for. +func FilterUInt32(s []uint32, cb func(s uint32) bool) []uint32 { + results := []uint32{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterUInt64 iterates over a collection of uint64, returning an array of +// all uint64 elements predicate returns truthy for. +func FilterUInt64(s []uint64, cb func(s uint64) bool) []uint64 { + results := []uint64{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// FilterString iterates over a collection of string, returning an array of +// all string elements predicate returns truthy for. +func FilterString(s []string, cb func(s string) bool) []string { + results := []string{} + + for _, i := range s { + result := cb(i) + + if result { + results = append(results, i) + } + } + + return results +} + +// ContainsInt returns true if an int is present in a iteratee. +func ContainsInt(s []int, v int) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsInt32 returns true if an int32 is present in a iteratee. +func ContainsInt32(s []int32, v int32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsInt64 returns true if an int64 is present in a iteratee. +func ContainsInt64(s []int64, v int64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt returns true if an uint is present in a iteratee. +func ContainsUInt(s []uint, v uint) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt32 returns true if an uint32 is present in a iteratee. +func ContainsUInt32(s []uint32, v uint32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsUInt64 returns true if an uint64 is present in a iteratee. +func ContainsUInt64(s []uint64, v uint64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + + +// ContainsString returns true if a string is present in a iteratee. +func ContainsString(s []string, v string) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsFloat32 returns true if a float32 is present in a iteratee. +func ContainsFloat32(s []float32, v float32) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// ContainsFloat64 returns true if a float64 is present in a iteratee. +func ContainsFloat64(s []float64, v float64) bool { + for _, vv := range s { + if vv == v { + return true + } + } + return false +} + +// SumInt32 sums a int32 iteratee and returns the sum of all elements +func SumInt32(s []int32) (sum int32) { + for _, v := range s { + sum += v + } + return +} + +// SumInt64 sums a int64 iteratee and returns the sum of all elements +func SumInt64(s []int64) (sum int64) { + for _, v := range s { + sum += v + } + return +} + +// SumInt sums a int iteratee and returns the sum of all elements +func SumInt(s []int) (sum int) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt32 sums a uint32 iteratee and returns the sum of all elements +func SumUInt32(s []uint32) (sum uint32) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt64 sums a uint64 iteratee and returns the sum of all elements +func SumUInt64(s []uint64) (sum uint64) { + for _, v := range s { + sum += v + } + return +} + +// SumUInt sums a uint iteratee and returns the sum of all elements +func SumUInt(s []uint) (sum uint) { + for _, v := range s { + sum += v + } + return +} + +// SumFloat64 sums a float64 iteratee and returns the sum of all elements +func SumFloat64(s []float64) (sum float64) { + for _, v := range s { + sum += v + } + return +} + +// SumFloat32 sums a float32 iteratee and returns the sum of all elements +func SumFloat32(s []float32) (sum float32) { + for _, v := range s { + sum += v + } + return +} + +// ReverseStrings reverses an array of string +func ReverseStrings(s []string) []string { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt reverses an array of int +func ReverseInt(s []int) []int { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt32 reverses an array of int32 +func ReverseInt32(s []int32) []int32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseInt64 reverses an array of int64 +func ReverseInt64(s []int64) []int64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt reverses an array of int +func ReverseUInt(s []uint) []uint { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt32 reverses an array of uint32 +func ReverseUInt32(s []uint32) []uint32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseUInt64 reverses an array of uint64 +func ReverseUInt64(s []uint64) []uint64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseFloat64 reverses an array of float64 +func ReverseFloat64(s []float64) []float64 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseFloat32 reverses an array of float32 +func ReverseFloat32(s []float32) []float32 { + for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + return s +} + +// ReverseString reverses a string +func ReverseString(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +func indexOf(n int, f func(int) bool) int { + for i := 0; i < n; i++ { + if f(i) { + return i + } + } + return -1 +} + +// IndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 +// if the value cannot be found +func IndexOfInt(a []int, x int) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 +// if the value cannot be found +func IndexOfInt32(a []int32, x int32) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 +// if the value cannot be found +func IndexOfInt64(a []int64, x int64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt(a []uint, x uint) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt32(a []uint32, x uint32) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 +// if the value cannot be found +func IndexOfUInt64(a []uint64, x uint64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 +// if the value cannot be found +func IndexOfFloat64(a []float64, x float64) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// IndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 +// if the value cannot be found +func IndexOfString(a []string, x string) int { + return indexOf(len(a), func(i int) bool { return a[i] == x }) +} + +func lastIndexOf(n int, f func(int) bool) int { + for i := n - 1; i >= 0; i-- { + if f(i) { + return i + } + } + return -1 +} + +// LastIndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt(a []int, x int) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt32(a []int32, x int32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfInt64(a []int64, x int64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt(a []uint, x uint) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt32(a []uint32, x uint32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfUInt64(a []uint64, x uint64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfFloat64(a []float64, x float64) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfFloat32 gets the index at which the first occurrence of an float32 value is found in array or return -1 +// if the value cannot be found +func LastIndexOfFloat32(a []float32, x float32) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// LastIndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 +// if the value cannot be found +func LastIndexOfString(a []string, x string) int { + return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) +} + +// UniqInt32 creates an array of int32 with unique values. +func UniqInt32(a []int32) []int32 { + length := len(a) + + seen := make(map[int32]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqInt64 creates an array of int64 with unique values. +func UniqInt64(a []int64) []int64 { + length := len(a) + + seen := make(map[int64]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqInt creates an array of int with unique values. +func UniqInt(a []int) []int { + length := len(a) + + seen := make(map[int]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqUInt32 creates an array of uint32 with unique values. +func UniqUInt32(a []uint32) []uint32 { + length := len(a) + + seen := make(map[uint32]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqUInt64 creates an array of uint64 with unique values. +func UniqUInt64(a []uint64) []uint64 { + length := len(a) + + seen := make(map[uint64]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqUInt creates an array of uint with unique values. +func UniqUInt(a []uint) []uint { + length := len(a) + + seen := make(map[uint]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqString creates an array of string with unique values. +func UniqString(a []string) []string { + length := len(a) + + seen := make(map[string]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqFloat64 creates an array of float64 with unique values. +func UniqFloat64(a []float64) []float64 { + length := len(a) + + seen := make(map[float64]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// UniqFloat32 creates an array of float32 with unique values. +func UniqFloat32(a []float32) []float32 { + length := len(a) + + seen := make(map[float32]struct{}, length) + j := 0 + + for i := 0; i < length; i++ { + v := a[i] + + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + a[j] = v + j++ + } + + return a[0:j] +} + +// ShuffleInt creates an array of int shuffled values using Fisher–Yates algorithm +func ShuffleInt(a []int) []int { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleInt32 creates an array of int32 shuffled values using Fisher–Yates algorithm +func ShuffleInt32(a []int32) []int32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleInt64 creates an array of int64 shuffled values using Fisher–Yates algorithm +func ShuffleInt64(a []int64) []int64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt creates an array of int shuffled values using Fisher–Yates algorithm +func ShuffleUInt(a []uint) []uint { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt32 creates an array of uint32 shuffled values using Fisher–Yates algorithm +func ShuffleUInt32(a []uint32) []uint32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleUInt64 creates an array of uint64 shuffled values using Fisher–Yates algorithm +func ShuffleUInt64(a []uint64) []uint64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleString creates an array of string shuffled values using Fisher–Yates algorithm +func ShuffleString(a []string) []string { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleFloat32 creates an array of float32 shuffled values using Fisher–Yates algorithm +func ShuffleFloat32(a []float32) []float32 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// ShuffleFloat64 creates an array of float64 shuffled values using Fisher–Yates algorithm +func ShuffleFloat64(a []float64) []float64 { + for i := range a { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } + + return a +} + +// DropString creates a slice with `n` strings dropped from the beginning. +func DropString(s []string, n int) []string { + return s[n:] +} + +// DropInt creates a slice with `n` ints dropped from the beginning. +func DropInt(s []int, n int) []int { + return s[n:] +} + +// DropInt32 creates a slice with `n` int32s dropped from the beginning. +func DropInt32(s []int32, n int) []int32 { + return s[n:] +} + +// DropInt64 creates a slice with `n` int64s dropped from the beginning. +func DropInt64(s []int64, n int) []int64 { + return s[n:] +} + +// DropUInt creates a slice with `n` ints dropped from the beginning. +func DropUInt(s []uint, n uint) []uint { + return s[n:] +} + +// DropUInt32 creates a slice with `n` int32s dropped from the beginning. +func DropUInt32(s []uint32, n int) []uint32 { + return s[n:] +} + +// DropUInt64 creates a slice with `n` int64s dropped from the beginning. +func DropUInt64(s []uint64, n int) []uint64 { + return s[n:] +} + +// DropFloat32 creates a slice with `n` float32s dropped from the beginning. +func DropFloat32(s []float32, n int) []float32 { + return s[n:] +} + +// DropFloat64 creates a slice with `n` float64s dropped from the beginning. +func DropFloat64(s []float64, n int) []float64 { + return s[n:] +} diff --git a/vendor/github.com/thoas/go-funk/utils.go b/vendor/github.com/thoas/go-funk/utils.go new file mode 100644 index 0000000000000..43d9a2be73a9d --- /dev/null +++ b/vendor/github.com/thoas/go-funk/utils.go @@ -0,0 +1,103 @@ +package funk + +import ( + "fmt" + "reflect" +) + +func equal(expectedOrPredicate interface{}, optionalIsMap ...bool) func(keyValueIfMap, actualValue reflect.Value) bool { + isMap := append(optionalIsMap, false)[0] + + if IsFunction(expectedOrPredicate) { + inTypes := []reflect.Type{nil}; if isMap { + inTypes = append(inTypes, nil) + } + + if !IsPredicate(expectedOrPredicate, inTypes...) { + panic(fmt.Sprintf("Predicate function must have %d parameter and must return boolean", len(inTypes))) + } + + predicateValue := reflect.ValueOf(expectedOrPredicate) + + return func(keyValueIfMap, actualValue reflect.Value) bool { + + if isMap && !keyValueIfMap.Type().ConvertibleTo(predicateValue.Type().In(0)) { + panic("Given key is not compatible with type of parameter for the predicate.") + } + + if (isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(1))) || + (!isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(0))) { + panic("Given value is not compatible with type of parameter for the predicate.") + } + + args := []reflect.Value{actualValue} + if isMap { + args = append([]reflect.Value{keyValueIfMap}, args...) + } + + return predicateValue.Call(args)[0].Bool() + } + } + + expected := expectedOrPredicate + + return func(keyValueIfMap, actualValue reflect.Value) bool { + if isMap { + actualValue = keyValueIfMap + } + + if expected == nil || actualValue.IsZero() { + return actualValue.Interface() == expected + } + + return reflect.DeepEqual(actualValue.Interface(), expected) + } +} + +func sliceElem(rtype reflect.Type) reflect.Type { + for { + if rtype.Kind() != reflect.Slice && rtype.Kind() != reflect.Array { + return rtype + } + + rtype = rtype.Elem() + } +} + +func redirectValue(value reflect.Value) reflect.Value { + for { + if !value.IsValid() || (value.Kind() != reflect.Ptr && value.Kind() != reflect.Interface) { + return value + } + + res := value.Elem() + + // Test for a circular type. + if res.Kind() == reflect.Ptr && value.Kind() == reflect.Ptr && value.Pointer() == res.Pointer() { + return value + } + + if !res.IsValid() && value.Kind() == reflect.Ptr { + return reflect.Zero(value.Type().Elem()) + } + + value = res + } +} + +func makeSlice(value reflect.Value, values ...int) reflect.Value { + sliceType := sliceElem(value.Type()) + + size := value.Len() + cap := size + + if len(values) > 0 { + size = values[0] + } + + if len(values) > 1 { + cap = values[1] + } + + return reflect.MakeSlice(reflect.SliceOf(sliceType), size, cap) +} diff --git a/vendor/github.com/thoas/go-funk/without.go b/vendor/github.com/thoas/go-funk/without.go new file mode 100644 index 0000000000000..6e35e98699332 --- /dev/null +++ b/vendor/github.com/thoas/go-funk/without.go @@ -0,0 +1,19 @@ +package funk + +import "reflect" + +// Without creates an array excluding all given values. +func Without(in interface{}, values ...interface{}) interface{} { + if !IsCollection(in) { + panic("First parameter must be a collection") + } + + inValue := reflect.ValueOf(in) + for _, value := range values { + if NotEqual(inValue.Type().Elem(), reflect.TypeOf(value)) { + panic("Values must have the same type") + } + } + + return LeftJoin(inValue, reflect.ValueOf(values)).Interface() +} diff --git a/vendor/github.com/thoas/go-funk/zip.go b/vendor/github.com/thoas/go-funk/zip.go new file mode 100644 index 0000000000000..f9056bc419bcd --- /dev/null +++ b/vendor/github.com/thoas/go-funk/zip.go @@ -0,0 +1,46 @@ +package funk + +import ( + "reflect" +) + +// Tuple is the return type of Zip +type Tuple struct { + Element1 interface{} + Element2 interface{} +} + +// Zip returns a list of tuples, where the i-th tuple contains the i-th element +// from each of the input iterables. The returned list is truncated in length +// to the length of the shortest input iterable. +func Zip(slice1 interface{}, slice2 interface{}) []Tuple { + inValue1 := reflect.ValueOf(slice1) + inValue2 := reflect.ValueOf(slice2) + kind1 := inValue1.Type().Kind() + kind2 := inValue2.Type().Kind() + + result := []Tuple{} + for _, kind := range []reflect.Kind{kind1, kind2} { + if kind != reflect.Slice && kind != reflect.Array { + return result + } + } + + var minLength int + length1 := inValue1.Len() + length2 := inValue2.Len() + if length1 <= length2 { + minLength = length1 + } else { + minLength = length2 + } + + for i := 0; i < minLength; i++ { + newTuple := Tuple{ + Element1: inValue1.Index(i).Interface(), + Element2: inValue2.Index(i).Interface(), + } + result = append(result, newTuple) + } + return result +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 41b6e2199fef0..01007e990cb11 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -761,6 +761,9 @@ github.com/syndtr/goleveldb/leveldb/opt github.com/syndtr/goleveldb/leveldb/storage github.com/syndtr/goleveldb/leveldb/table github.com/syndtr/goleveldb/leveldb/util +# github.com/thoas/go-funk v0.8.0 +## explicit +github.com/thoas/go-funk # github.com/toqueteos/webbrowser v1.2.0 github.com/toqueteos/webbrowser # github.com/tstranex/u2f v1.0.0 diff --git a/web_src/js/index.js b/web_src/js/index.js index 7a76b89f9b584..0133ee85f44c1 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -2069,12 +2069,22 @@ function initAdmin() { } } - function onVerifyGroupMembershipChange() { - if ($('#groups_enabled').is(':checked')) { + function onGroupOptionsChange() { + if ($('#groups_enabled').is(':checked') || $('#team_group_map_enabled').is(':checked') || $('#team_group_map_removal').is(':checked')) { $('#groups_enabled_change').show(); } else { $('#groups_enabled_change').hide(); } + if ($('#groups_enabled').is(':checked')) { + $('#group_filter_field').show(); + } else { + $('#group_filter_field').hide(); + } + if ($('#team_group_map_enabled').is(':checked') || $('#team_group_map_removal').is(':checked')) { + $('#team_group_map_field').show(); + } else { + $('#team_group_map_field').hide(); + } } // New authentication @@ -2117,7 +2127,7 @@ function initAdmin() { } if (authType === '2' || authType === '5') { onSecurityProtocolChange(); - onVerifyGroupMembershipChange(); + onGroupOptionsChange(); } if (authType === '2') { onUsePagedSearchChange(); @@ -2128,15 +2138,15 @@ function initAdmin() { $('#use_paged_search').on('change', onUsePagedSearchChange); $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); + $('.ldap_group_options').on('change', onGroupOptionsChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { const authType = $('#auth_type').val(); if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); - onVerifyGroupMembershipChange(); + $('.ldap_group_options').on('change', onGroupOptionsChange); + onGroupOptionsChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); } From 673df9974183a8cb5c7fef028599dd5275749d4b Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Fri, 2 Jul 2021 11:08:41 +0200 Subject: [PATCH 02/19] Add tests to LDAP group sync * Adding and removing team members. * Sync not existing LDAP group. * Login with broken group map JSON. Co-authored-by: Giuliano Mele Co-authored-by: Sven Seeberg --- integrations/auth_ldap_test.go | 134 +++++++++++++++++++++++++++++++++ modules/auth/ldap/ldap.go | 2 +- 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index 4d82c092e7280..0d6e8e92f9f85 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -119,6 +119,12 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) { "attribute_ssh_public_key": sshKeyAttribute, "is_sync_enabled": "on", "is_active": "on", + "team_group_map_enabled": "on", + "team_group_map_removal": "on", + "group_dn": "ou=people,dc=planetexpress,dc=com", + "group_member_uid": "member", + "user_uid": "DN", + "team_group_map": "{\"cn=ship_crew,ou=people,dc=planetexpress,dc=com\": {\"org26\": [\"team11\"]},\"cn=admin_staff,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]},\"cn=non-existent,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]}}", }) session.MakeRequest(t, req, http.StatusFound) } @@ -240,3 +246,131 @@ func TestLDAPUserSSHKeySync(t *testing.T) { assert.ElementsMatch(t, u.SSHKeys, syncedKeys, "Unequal number of keys synchronized for user: %s", u.UserName) } } + +func TestLDAPGroupTeamSyncAddMember(t *testing.T) { + if skipLDAPTests() { + t.Skip() + return + } + defer prepareTestEnv(t)() + addAuthSourceLDAP(t, "") + org, err := models.GetOrgByName("org26") + assert.NoError(t, err) + team, err := models.GetTeam(org.ID, "team11") + assert.NoError(t, err) + models.SyncExternalUsers(context.Background(), true) + for _, gitLDAPUser := range gitLDAPUsers { + user := models.AssertExistsAndLoadBean(t, &models.User{ + Name: gitLDAPUser.UserName, + }).(*models.User) + usersOrgs, err := models.GetOrgsByUserID(user.ID, true) + assert.NoError(t, err) + allOrgTeams, err := models.GetUserOrgTeams(org.ID, user.ID) + assert.NoError(t, err) + if user.Name == "fry" || user.Name == "leela" || user.Name == "bender" { + // assert members of LDAP group "cn=ship_crew" are added to mapped teams + assert.Equal(t, len(usersOrgs), 1, "User should be member of one organization") + assert.Equal(t, usersOrgs[0].Name, "org26", "Membership should be added to the right organization") + isMember, err := models.IsTeamMember(usersOrgs[0].ID, team.ID, user.ID) + assert.NoError(t, err) + assert.True(t, isMember, "Membership should be added to the right team") + err = team.RemoveMember(user.ID) + assert.NoError(t, err) + } else { + // assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist + assert.Empty(t, usersOrgs, "User should be member of no organization") + isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID) + assert.NoError(t, err) + assert.False(t, isMember, "User should no be added to this team") + assert.Empty(t, allOrgTeams, "User should not be added to any team") + } + } +} + +func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { + if skipLDAPTests() { + t.Skip() + return + } + defer prepareTestEnv(t)() + addAuthSourceLDAP(t, "") + models.SyncExternalUsers(context.Background(), true) + org, err := models.GetOrgByName("org26") + assert.NoError(t, err) + team, err := models.GetTeam(org.ID, "team11") + assert.NoError(t, err) + user, err := models.GetUserByName("professor") + assert.NoError(t, err) + err = org.AddMember(user.ID) + assert.NoError(t, err) + err = team.AddMember(user.ID) + assert.NoError(t, err) + isMember, err := models.IsOrganizationMember(org.ID, user.ID) + assert.NoError(t, err) + assert.True(t, isMember, "User should be member of this organization") + isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID) + assert.NoError(t, err) + assert.True(t, isMember, "User should be member of this team") + // assert team member "professor" gets removed from "team11" + models.SyncExternalUsers(context.Background(), true) + isMember, err = models.IsOrganizationMember(org.ID, user.ID) + assert.NoError(t, err) + assert.False(t, isMember, "User membership should have been removed from organization") + isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID) + assert.NoError(t, err) + assert.False(t, isMember, "User membership should have been removed from team") +} + +func addBrokenLDAPMapAuthSource(t *testing.T, sshKeyAttribute string) { + session := loginUser(t, "user1") + csrf := GetCSRF(t, session, "/admin/auths/new") + req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{ + "_csrf": csrf, + "type": "2", + "name": "ldap", + "host": getLDAPServerHost(), + "port": "389", + "bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com", + "bind_password": "password", + "user_base": "ou=people,dc=planetexpress,dc=com", + "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))", + "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)", + "restricted_filter": "(uid=leela)", + "attribute_username": "uid", + "attribute_name": "givenName", + "attribute_surname": "sn", + "attribute_mail": "mail", + "attribute_ssh_public_key": sshKeyAttribute, + "is_sync_enabled": "on", + "is_active": "on", + "team_group_map_enabled": "on", + "team_group_map_removal": "on", + "group_dn": "ou=people,dc=planetexpress,dc=com", + "group_member_uid": "member", + "user_uid": "DN", + "team_group_map": "{\"NOT_A_VALID_JSON\"[\"MISSING_DOUBLE_POINT\"]}", + }) + session.MakeRequest(t, req, http.StatusFound) +} + +// Login should work even if Team Group Map contains a broken JSON +func TestBrokenLDAPMapUserSignin(t *testing.T) { + if skipLDAPTests() { + t.Skip() + return + } + defer prepareTestEnv(t)() + addBrokenLDAPMapAuthSource(t, "") + + u := gitLDAPUsers[0] + + session := loginUserWithPassword(t, u.UserName, u.Password) + req := NewRequest(t, "GET", "/user/settings") + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + + assert.Equal(t, u.UserName, htmlDoc.GetInputValueByName("name")) + assert.Equal(t, u.FullName, htmlDoc.GetInputValueByName("full_name")) + assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text()) +} diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 92eceed025585..41f3fb6e21e26 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -274,7 +274,7 @@ func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { err := json.Unmarshal([]byte(ls.TeamGroupMap), &ldapGroupsToTeams) if err != nil { log.Debug("Failed to unmarshall LDAP teams map: %v", err) - return nil + return ldapGroupsToTeams } return ldapGroupsToTeams } From 3a032cc9875e1043e8da64fb4c4450c703013e91 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Thu, 26 Aug 2021 15:24:32 +0200 Subject: [PATCH 03/19] Replace funk package by custom utility Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- go.mod | 1 - go.sum | 2 - modules/auth/ldap/ldap.go | 10 +- modules/util/map.go | 26 + modules/util/slice.go | 27 + vendor/github.com/thoas/go-funk/.gitignore | 27 - vendor/github.com/thoas/go-funk/.travis.yml | 7 - vendor/github.com/thoas/go-funk/CHANGELOG.md | 29 - vendor/github.com/thoas/go-funk/LICENSE | 21 - vendor/github.com/thoas/go-funk/Makefile | 5 - vendor/github.com/thoas/go-funk/README.rst | 835 --------------- vendor/github.com/thoas/go-funk/assign.go | 129 --- vendor/github.com/thoas/go-funk/builder.go | 110 -- .../github.com/thoas/go-funk/chain_builder.go | 142 --- vendor/github.com/thoas/go-funk/compact.go | 50 - vendor/github.com/thoas/go-funk/fill.go | 34 - vendor/github.com/thoas/go-funk/go.mod | 5 - vendor/github.com/thoas/go-funk/go.sum | 11 - vendor/github.com/thoas/go-funk/helpers.go | 317 ------ .../github.com/thoas/go-funk/intersection.go | 253 ----- vendor/github.com/thoas/go-funk/join.go | 86 -- .../thoas/go-funk/join_primitives.go | 385 ------- .../github.com/thoas/go-funk/lazy_builder.go | 117 --- vendor/github.com/thoas/go-funk/map.go | 74 -- vendor/github.com/thoas/go-funk/max.go | 194 ---- vendor/github.com/thoas/go-funk/min.go | 193 ---- vendor/github.com/thoas/go-funk/operation.go | 69 -- .../github.com/thoas/go-funk/permutation.go | 29 - vendor/github.com/thoas/go-funk/predicate.go | 47 - vendor/github.com/thoas/go-funk/presence.go | 207 ---- vendor/github.com/thoas/go-funk/reduce.go | 87 -- vendor/github.com/thoas/go-funk/retrieve.go | 93 -- vendor/github.com/thoas/go-funk/scan.go | 192 ---- vendor/github.com/thoas/go-funk/short_if.go | 8 - vendor/github.com/thoas/go-funk/subset.go | 41 - .../github.com/thoas/go-funk/subtraction.go | 87 -- vendor/github.com/thoas/go-funk/transform.go | 555 ---------- vendor/github.com/thoas/go-funk/typesafe.go | 967 ------------------ vendor/github.com/thoas/go-funk/utils.go | 103 -- vendor/github.com/thoas/go-funk/without.go | 19 - vendor/github.com/thoas/go-funk/zip.go | 46 - vendor/modules.txt | 3 - 42 files changed, 58 insertions(+), 5585 deletions(-) create mode 100644 modules/util/map.go create mode 100644 modules/util/slice.go delete mode 100644 vendor/github.com/thoas/go-funk/.gitignore delete mode 100644 vendor/github.com/thoas/go-funk/.travis.yml delete mode 100644 vendor/github.com/thoas/go-funk/CHANGELOG.md delete mode 100644 vendor/github.com/thoas/go-funk/LICENSE delete mode 100644 vendor/github.com/thoas/go-funk/Makefile delete mode 100644 vendor/github.com/thoas/go-funk/README.rst delete mode 100644 vendor/github.com/thoas/go-funk/assign.go delete mode 100644 vendor/github.com/thoas/go-funk/builder.go delete mode 100644 vendor/github.com/thoas/go-funk/chain_builder.go delete mode 100644 vendor/github.com/thoas/go-funk/compact.go delete mode 100644 vendor/github.com/thoas/go-funk/fill.go delete mode 100644 vendor/github.com/thoas/go-funk/go.mod delete mode 100644 vendor/github.com/thoas/go-funk/go.sum delete mode 100644 vendor/github.com/thoas/go-funk/helpers.go delete mode 100644 vendor/github.com/thoas/go-funk/intersection.go delete mode 100644 vendor/github.com/thoas/go-funk/join.go delete mode 100644 vendor/github.com/thoas/go-funk/join_primitives.go delete mode 100644 vendor/github.com/thoas/go-funk/lazy_builder.go delete mode 100644 vendor/github.com/thoas/go-funk/map.go delete mode 100644 vendor/github.com/thoas/go-funk/max.go delete mode 100644 vendor/github.com/thoas/go-funk/min.go delete mode 100644 vendor/github.com/thoas/go-funk/operation.go delete mode 100644 vendor/github.com/thoas/go-funk/permutation.go delete mode 100644 vendor/github.com/thoas/go-funk/predicate.go delete mode 100644 vendor/github.com/thoas/go-funk/presence.go delete mode 100644 vendor/github.com/thoas/go-funk/reduce.go delete mode 100644 vendor/github.com/thoas/go-funk/retrieve.go delete mode 100644 vendor/github.com/thoas/go-funk/scan.go delete mode 100644 vendor/github.com/thoas/go-funk/short_if.go delete mode 100644 vendor/github.com/thoas/go-funk/subset.go delete mode 100644 vendor/github.com/thoas/go-funk/subtraction.go delete mode 100644 vendor/github.com/thoas/go-funk/transform.go delete mode 100644 vendor/github.com/thoas/go-funk/typesafe.go delete mode 100644 vendor/github.com/thoas/go-funk/utils.go delete mode 100644 vendor/github.com/thoas/go-funk/without.go delete mode 100644 vendor/github.com/thoas/go-funk/zip.go diff --git a/go.mod b/go.mod index 2bb6f9b7ba52e..5032acce990fe 100644 --- a/go.mod +++ b/go.mod @@ -106,7 +106,6 @@ require ( github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 - github.com/thoas/go-funk v0.8.0 github.com/tstranex/u2f v1.0.0 github.com/ulikunitz/xz v0.5.10 // indirect github.com/unknwon/com v1.0.1 diff --git a/go.sum b/go.sum index 077560747b4e5..fc6e2422c8496 100644 --- a/go.sum +++ b/go.sum @@ -1018,8 +1018,6 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/thoas/go-funk v0.8.0 h1:JP9tKSvnpFVclYgDM0Is7FD9M4fhPvqA0s0BsXmzSRQ= -github.com/thoas/go-funk v0.8.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go index 41f3fb6e21e26..8530796ab7abc 100644 --- a/modules/auth/ldap/ldap.go +++ b/modules/auth/ldap/ldap.go @@ -14,9 +14,9 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" "github.com/go-ldap/ldap/v3" - "github.com/thoas/go-funk" ) // SecurityProtocol protocol type @@ -273,7 +273,7 @@ func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { ldapGroupsToTeams := make(map[string]map[string][]string) err := json.Unmarshal([]byte(ls.TeamGroupMap), &ldapGroupsToTeams) if err != nil { - log.Debug("Failed to unmarshall LDAP teams map: %v", err) + log.Error("Failed to unmarshall LDAP teams map: %v", err) return ldapGroupsToTeams } return ldapGroupsToTeams @@ -287,11 +287,11 @@ func (ls *Source) getMappedTeams(l *ldap.Conn, uid string) (map[string][]string, // unmarshall LDAP group team map from configs ldapGroupsToTeams := ls.mapLdapGroupsToTeams() // select all LDAP groups from settings - allLdapGroups := funk.Keys(ldapGroupsToTeams).([]string) + allLdapGroups := util.KeysString(ldapGroupsToTeams).([]string) // contains LDAP config groups, which the user is a member of - usersLdapGroupsToAdd := funk.IntersectString(allLdapGroups, usersLdapGroups) + usersLdapGroupsToAdd := util.IntersectString(allLdapGroups, usersLdapGroups) // contains LDAP config groups, which the user is not a member of - usersLdapGroupToRemove, _ := funk.DifferenceString(allLdapGroups, usersLdapGroups) + usersLdapGroupToRemove := util.DifferenceString(allLdapGroups, usersLdapGroups) for _, groupToAdd := range usersLdapGroupsToAdd { for k, v := range ldapGroupsToTeams[groupToAdd] { teamsToAdd[k] = v diff --git a/modules/util/map.go b/modules/util/map.go new file mode 100644 index 0000000000000..75f49e1c91c6a --- /dev/null +++ b/modules/util/map.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "reflect" +) + +// KeysString returns a slice of keys from a map, dict must be a map +func KeysString(dict interface{}) interface{} { + value := reflect.ValueOf(dict) + valueType := value.Type() + if value.Kind() == reflect.Map { + keys := value.MapKeys() + length := len(keys) + resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) + for i, key := range keys { + resultSlice.Index(i).Set(key) + } + return resultSlice.Interface() + } + panic(fmt.Sprintf("Type %s is not supported by KeysString", valueType.String())) +} diff --git a/modules/util/slice.go b/modules/util/slice.go new file mode 100644 index 0000000000000..1018f2ff6dece --- /dev/null +++ b/modules/util/slice.go @@ -0,0 +1,27 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package util + +// IntersectString returns the intersection of the two string slices +func IntersectString(a, b []string) []string { + var intersection []string + for _, v := range a { + if ExistsInSlice(v, b) && !ExistsInSlice(v, intersection) { + intersection = append(intersection, v) + } + } + return intersection +} + +// DifferenceString returns all elements of slice a which are not present in slice b +func DifferenceString(a, b []string) []string { + var difference []string + for _, v := range a { + if !ExistsInSlice(v, b) && !ExistsInSlice(v, difference) { + difference = append(difference, v) + } + } + return difference +} diff --git a/vendor/github.com/thoas/go-funk/.gitignore b/vendor/github.com/thoas/go-funk/.gitignore deleted file mode 100644 index 7b4c598b0b9fb..0000000000000 --- a/vendor/github.com/thoas/go-funk/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -#GoLand -.idea \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/.travis.yml b/vendor/github.com/thoas/go-funk/.travis.yml deleted file mode 100644 index dbc0f7267f8e5..0000000000000 --- a/vendor/github.com/thoas/go-funk/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: go -before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/stretchr/testify -go: - - "1.14" -script: make test diff --git a/vendor/github.com/thoas/go-funk/CHANGELOG.md b/vendor/github.com/thoas/go-funk/CHANGELOG.md deleted file mode 100644 index 000a371bf4291..0000000000000 --- a/vendor/github.com/thoas/go-funk/CHANGELOG.md +++ /dev/null @@ -1,29 +0,0 @@ -go-funk changelog -================= - -0.1 (2017-01-18) ----------------- - -Changes can be seen [here](https://github.com/thoas/go-funk/compare/73b8ae1f6443c9d4acbdc612bbb2ca804bb39b1d...master) - -* Better test suite -* Better documentation -* Add typesafe implementations: - - * ``Contains`` - * ``Sum`` - * ``Reverse`` - * ``IndexOf`` - * ``Uniq`` - * ``Shuffle`` -* Add benchmarks - - * ``Contains`` - * ``Uniq`` - * ``Sum`` -* Fix ``redirectValue`` when using a circular reference -* Add ``Sum`` generic implementation which computes the sum of values in an array -* Add ``Tail`` generic implementation to retrieve all but the first element of array -* Add ``Initial`` generic implementation to retrieve all but the last element of array -* Add ``Last`` generic implementation to retrieve the last element of an array -* Add ``Head`` generic implementation to retrieve the first element of an array diff --git a/vendor/github.com/thoas/go-funk/LICENSE b/vendor/github.com/thoas/go-funk/LICENSE deleted file mode 100644 index 2430978a06793..0000000000000 --- a/vendor/github.com/thoas/go-funk/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 Florent Messa - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/thoas/go-funk/Makefile b/vendor/github.com/thoas/go-funk/Makefile deleted file mode 100644 index 2fed2b2e54aff..0000000000000 --- a/vendor/github.com/thoas/go-funk/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -test: - go test -v - -bench: - go test -benchmem -bench . diff --git a/vendor/github.com/thoas/go-funk/README.rst b/vendor/github.com/thoas/go-funk/README.rst deleted file mode 100644 index 2f6b539874c2f..0000000000000 --- a/vendor/github.com/thoas/go-funk/README.rst +++ /dev/null @@ -1,835 +0,0 @@ -go-funk -======= - -.. image:: https://secure.travis-ci.org/thoas/go-funk.svg?branch=master - :alt: Build Status - :target: http://travis-ci.org/thoas/go-funk - -.. image:: https://godoc.org/github.com/thoas/go-funk?status.svg - :alt: GoDoc - :target: https://pkg.go.dev/github.com/thoas/go-funk - -.. image:: https://goreportcard.com/badge/github.com/thoas/go-funk - :alt: Go report - :target: https://goreportcard.com/report/github.com/thoas/go-funk - -``go-funk`` is a modern Go library based on reflect_. - -Generic helpers rely on reflect_, be careful this code runs exclusively on runtime so you must have a good test suite. - -These helpers have started as an experiment to learn reflect_. It may look like lodash_ in some aspects but -it will have its own roadmap. lodash_ is an awesome library with a lot of work behind it, all features included in -``go-funk`` come from internal use cases. - -You can also find typesafe implementation in the godoc_. - -Why this name? --------------- - -Long story, short answer because ``func`` is a reserved word in Go, I wanted something similar. - -Initially this project was named ``fn`` I don't need to explain why that was a bad idea for french speakers :) - -Let's ``funk``! - -.. image:: https://media.giphy.com/media/3oEjHQKtDXpeGN9rW0/giphy.gif - -<3 - -Installation ------------- - -.. code-block:: bash - - go get github.com/thoas/go-funk - -Usage ------ - -.. code-block:: go - - import "github.com/thoas/go-funk" - -These examples will be based on the following data model: - -.. code-block:: go - - type Foo struct { - ID int - FirstName string `tag_name:"tag 1"` - LastName string `tag_name:"tag 2"` - Age int `tag_name:"tag 3"` - } - - func (f Foo) TableName() string { - return "foo" - } - -With fixtures: - -.. code-block:: go - - f := &Foo{ - ID: 1, - FirstName: "Foo", - LastName: "Bar", - Age: 30, - } - -You can import ``go-funk`` using a basic statement: - -.. code-block:: go - - import "github.com/thoas/go-funk" - -funk.Contains -............. - -Returns true if an element is present in a iteratee (slice, map, string). - -One frustrating thing in Go is to implement ``contains`` methods for each type, for example: - -.. code-block:: go - - func ContainsInt(s []int, e int) bool { - for _, a := range s { - if a == e { - return true - } - } - return false - } - -this can be replaced by ``funk.Contains``: - -.. code-block:: go - - // slice of string - funk.Contains([]string{"foo", "bar"}, "bar") // true - - // slice of Foo ptr - funk.Contains([]*Foo{f}, f) // true - funk.Contains([]*Foo{f}, func (foo *Foo) bool { - return foo.ID == f.ID - }) // true - funk.Contains([]*Foo{f}, nil) // false - - b := &Foo{ - ID: 2, - FirstName: "Florent", - LastName: "Messa", - Age: 28, - } - - funk.Contains([]*Foo{f}, b) // false - - // string - funk.Contains("florent", "rent") // true - funk.Contains("florent", "foo") // false - - // even map - funk.Contains(map[int]string{1: "Florent"}, 1) // true - funk.Contains(map[int]string{1: "Florent"}, func(key int, name string) bool { - return key == 1 // or `name == "Florent"` for the value type - }) // true - -see also, typesafe implementations: ContainsInt_, ContainsInt64_, ContainsFloat32_, ContainsFloat64_, ContainsString_ - -.. _ContainsFloat32: https://godoc.org/github.com/thoas/go-funk#ContainsFloat32 -.. _ContainsFloat64: https://godoc.org/github.com/thoas/go-funk#ContainsFloat64 -.. _ContainsInt: https://godoc.org/github.com/thoas/go-funk#ContainsInt -.. _ContainsInt64: https://godoc.org/github.com/thoas/go-funk#ContainsInt64 -.. _ContainsString: https://godoc.org/github.com/thoas/go-funk#ContainsString - -funk.Intersect -.............. - -Returns the intersection between two collections. - -.. code-block:: go - - funk.Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{2, 4} - funk.Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"foo", "bar"} - -see also, typesafe implementations: IntersectString - -.. IntersectString: https://godoc.org/github.com/thoas/go-funk#IntersectString - - -funk.Difference -.............. - -Returns the difference between two collections. - -.. code-block:: go - - funk.Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{1, 3}, []int{6} - funk.Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"hello"}, []string{} - -see also, typesafe implementations: DifferenceString - -.. DifferenceString: https://godoc.org/github.com/thoas/go-funk#DifferenceString - - -funk.IndexOf -............ - -Gets the index at which the first occurrence of a value is found in an array or return -1 -if the value cannot be found. - -.. code-block:: go - - // slice of string - funk.IndexOf([]string{"foo", "bar"}, "bar") // 1 - funk.IndexOf([]string{"foo", "bar"}, func(value string) bool { - return value == "bar" - }) // 1 - funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1 - -see also, typesafe implementations: IndexOfInt_, IndexOfInt64_, IndexOfFloat32_, IndexOfFloat64_, IndexOfString_ - -.. _IndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat32 -.. _IndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat64 -.. _IndexOfInt: https://godoc.org/github.com/thoas/go-funk#IndexOfInt -.. _IndexOfInt64: https://godoc.org/github.com/thoas/go-funk#IndexOfInt64 -.. _IndexOfString: https://godoc.org/github.com/thoas/go-funk#IndexOfString - -funk.LastIndexOf -................ - -Gets the index at which the last occurrence of a value is found in an array or return -1 -if the value cannot be found. - -.. code-block:: go - - // slice of string - funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2 - funk.LastIndexOf([]string{"foo", "bar"}, func(value string) bool { - return value == "bar" - }) // 2 - funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1 - -see also, typesafe implementations: LastIndexOfInt_, LastIndexOfInt64_, LastIndexOfFloat32_, LastIndexOfFloat64_, LastIndexOfString_ - -.. _LastIndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat32 -.. _LastIndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat64 -.. _LastIndexOfInt: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt -.. _LastIndexOfInt64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt64 -.. _LastIndexOfString: https://godoc.org/github.com/thoas/go-funk#LastIndexOfString - -funk.ToMap -.......... - -Transforms a slice of structs to a map based on a ``pivot`` field. - -.. code-block:: go - - f := &Foo{ - ID: 1, - FirstName: "Gilles", - LastName: "Fabio", - Age: 70, - } - - b := &Foo{ - ID: 2, - FirstName: "Florent", - LastName: "Messa", - Age: 80, - } - - results := []*Foo{f, b} - - mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b} - -funk.Filter -........... - -Filters a slice based on a predicate. - -.. code-block:: go - - r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool { - return x%2 == 0 - }) // []int{2, 4} - -see also, typesafe implementations: FilterInt_, FilterInt64_, FilterFloat32_, FilterFloat64_, FilterString_ - -.. _FilterFloat32: https://godoc.org/github.com/thoas/go-funk#FilterFloat32 -.. _FilterFloat64: https://godoc.org/github.com/thoas/go-funk#FilterFloat64 -.. _FilterInt: https://godoc.org/github.com/thoas/go-funk#FilterInt -.. _FilterInt64: https://godoc.org/github.com/thoas/go-funk#FilterInt64 -.. _FilterString: https://godoc.org/github.com/thoas/go-funk#FilterString - -funk.Reduce -........... - -Reduces an iteratee based on an accumulator function or operation rune for numbers. - -.. code-block:: go - - // Using operation runes. '+' and '*' only supported. - r := funk.Reduce([]int{1, 2, 3, 4}, '+', float64(0)) // 10 - r := funk.Reduce([]int{1, 2, 3, 4}, '*', 1) // 24 - - // Using accumulator function - r := funk.Reduce([]int{1, 2, 3, 4}, func(acc float64, num int) float64 { - return acc + float64(num) - }, float64(0)) // 10 - - r := funk.Reduce([]int{1, 2, 3, 4}, func(acc string, num int) string { - return acc + fmt.Sprint(num) - }, "") // "1234" - -funk.Find -......... - -Finds an element in a slice based on a predicate. - -.. code-block:: go - - r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool { - return x%2 == 0 - }) // 2 - -see also, typesafe implementations: FindInt_, FindInt64_, FindFloat32_, FindFloat64_, FindString_ - -.. _FindFloat32: https://godoc.org/github.com/thoas/go-funk#FindFloat32 -.. _FindFloat64: https://godoc.org/github.com/thoas/go-funk#FindFloat64 -.. _FindInt: https://godoc.org/github.com/thoas/go-funk#FindInt -.. _FindInt64: https://godoc.org/github.com/thoas/go-funk#FindInt64 -.. _FindString: https://godoc.org/github.com/thoas/go-funk#FindString - -funk.Map -........ - -Manipulates an iteratee (map, slice) and transforms it to another type: - -* map -> slice -* map -> map -* slice -> map -* slice -> slice - -.. code-block:: go - - r := funk.Map([]int{1, 2, 3, 4}, func(x int) int { - return x * 2 - }) // []int{2, 4, 6, 8} - - r := funk.Map([]int{1, 2, 3, 4}, func(x int) string { - return "Hello" - }) // []string{"Hello", "Hello", "Hello", "Hello"} - - r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) { - return x, x - }) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4} - - mapping := map[int]string{ - 1: "Florent", - 2: "Gilles", - } - - r = funk.Map(mapping, func(k int, v string) int { - return k - }) // []int{1, 2} - - r = funk.Map(mapping, func(k int, v string) (string, string) { - return fmt.Sprintf("%d", k), v - }) // map[string]string{"1": "Florent", "2": "Gilles"} - -funk.FlatMap -............ - -Manipulates an iteratee (map, slice) and transforms it to to a flattened collection of another type: - -* map -> slice -* slice -> slice - -.. code-block:: go - - r := funk.FlatMap([][]int{{1, 2}, {3, 4}}, func(x []int) []int { - return append(x, 0) - }) // []int{1, 2, 0, 3, 4, 0} - - mapping := map[string][]int{ - "Florent": {1, 2}, - "Gilles": {3, 4}, - } - - r = funk.FlatMap(mapping, func(k string, v []int) []int { - return v - }) // []int{1, 2, 3, 4} - -funk.Get -........ - -Retrieves the value at path of struct(s) or map(s). - -.. code-block:: go - - var bar *Bar = &Bar{ - Name: "Test", - Bars: []*Bar{ - &Bar{ - Name: "Level1-1", - Bar: &Bar{ - Name: "Level2-1", - }, - }, - &Bar{ - Name: "Level1-2", - Bar: &Bar{ - Name: "Level2-2", - }, - }, - }, - } - - var foo *Foo = &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Age: 30, - Bar: bar, - Bars: []*Bar{ - bar, - bar, - }, - } - - funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} - funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} - funk.Get(foo, "Bar.Name") // Test - -``funk.Get`` also support ``map`` values: - -.. code-block:: go - - bar := map[string]interface{}{ - "Name": "Test", - } - - foo1 := map[string]interface{}{ - "ID": 1, - "FirstName": "Dark", - "LastName": "Vador", - "Age": 30, - "Bar": bar, - } - - foo2 := &map[string]interface{}{ - "ID": 1, - "FirstName": "Dark", - "LastName": "Vador", - "Age": 30, - } // foo2.Bar is nil - - funk.Get(bar, "Name") // "Test" - funk.Get([]map[string]interface{}{foo1, foo2}, "Bar.Name") // []string{"Test"} - funk.Get(foo2, "Bar.Name") // nil - - -``funk.Get`` also handles ``nil`` values: - -.. code-block:: go - - bar := &Bar{ - Name: "Test", - } - - foo1 := &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Age: 30, - Bar: bar, - } - - foo2 := &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Age: 30, - } // foo2.Bar is nil - - funk.Get([]*Foo{foo1, foo2}, "Bar.Name") // []string{"Test"} - funk.Get(foo2, "Bar.Name") // nil - - - -funk.GetOrElse -.............. - -Retrieves the value of the pointer or default. - -.. code-block:: go - - str := "hello world" - GetOrElse(&str, "foobar") // string{"hello world"} - GetOrElse(str, "foobar") // string{"hello world"} - GetOrElse(nil, "foobar") // string{"foobar"} - -funk.Set -........ -Set value at a path of a struct - -.. code-block:: go - - var bar Bar = Bar{ - Name: "level-0", - Bar: &Bar{ - Name: "level-1", - Bars: []*Bar{ - {Name: "level2-1"}, - {Name: "level2-2"}, - }, - }, - } - - _ = Set(&bar, "level-0-new", "Name") - fmt.Println(bar.Name) // "level-0-new" - - MustSet(&bar, "level-1-new", "Bar.Name") - fmt.Println(bar.Bar.Name) // "level-1-new" - - Set(&bar, "level-2-new", "Bar.Bars.Name") - fmt.Println(bar.Bar.Bars[0].Name) // "level-2-new" - fmt.Println(bar.Bar.Bars[1].Name) // "level-2-new" - -funk.MustSet -............ -Short hand for funk.Set if struct does not contain interface{} field type to discard errors. - -funk.Prune -.......... -Copy a struct with only selected fields. Slice is handled by pruning all elements. - -.. code-block:: go - - bar := &Bar{ - Name: "Test", - } - - foo1 := &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Bar: bar, - } - - pruned, _ := Prune(foo1, []string{"FirstName", "Bar.Name"}) - // *Foo{ - // ID: 0, - // FirstName: "Dark", - // LastName: "", - // Bar: &Bar{Name: "Test}, - // } - -funk.PruneByTag -.......... -Same functionality as funk.Prune, but uses struct tags instead of struct field names. - -funk.Keys -......... - -Creates an array of the own enumerable map keys or struct field names. - -.. code-block:: go - - funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed) - - foo := &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Age: 30, - } - - funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed) - -funk.Values -........... - -Creates an array of the own enumerable map values or struct field values. - -.. code-block:: go - - funk.Values(map[string]int{"one": 1, "two": 2}) // []string{1, 2} (iteration order is not guaranteed) - - foo := &Foo{ - ID: 1, - FirstName: "Dark", - LastName: "Vador", - Age: 30, - } - - funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed) - -funk.ForEach -............ - -Range over an iteratee (map, slice). - -.. code-block:: go - - funk.ForEach([]int{1, 2, 3, 4}, func(x int) { - fmt.Println(x) - }) - -funk.ForEachRight -............ - -Range over an iteratee (map, slice) from the right. - -.. code-block:: go - - results := []int{} - - funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) { - results = append(results, x) - }) - - fmt.Println(results) // []int{4, 3, 2, 1} - -funk.Chunk -.......... - -Creates an array of elements split into groups with the length of the size. -If array can't be split evenly, the final chunk will be the remaining element. - -.. code-block:: go - - funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}} - -funk.FlattenDeep -................ - -Recursively flattens an array. - -.. code-block:: go - - funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4} - -funk.Uniq -......... - -Creates an array with unique values. - -.. code-block:: go - - funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12} - -see also, typesafe implementations: UniqInt_, UniqInt64_, UniqFloat32_, UniqFloat64_, UniqString_ - -.. _UniqFloat32: https://godoc.org/github.com/thoas/go-funk#UniqFloat32 -.. _UniqFloat64: https://godoc.org/github.com/thoas/go-funk#UniqFloat64 -.. _UniqInt: https://godoc.org/github.com/thoas/go-funk#UniqInt -.. _UniqInt64: https://godoc.org/github.com/thoas/go-funk#UniqInt64 -.. _UniqString: https://godoc.org/github.com/thoas/go-funk#UniqString - -funk.Drop -......... - -Creates an array/slice with `n` elements dropped from the beginning. - -.. code-block:: go - - funk.Drop([]int{0, 0, 0, 0}, 3) // []int{0} - -see also, typesafe implementations: DropInt_, DropInt32_, DropInt64_, DropFloat32_, DropFloat64_, DropString_ - -.. _DropInt: https://godoc.org/github.com/thoas/go-funk#DropInt -.. _DropInt32: https://godoc.org/github.com/thoas/go-funk#DropInt64 -.. _DropInt64: https://godoc.org/github.com/thoas/go-funk#DropInt64 -.. _DropFloat32: https://godoc.org/github.com/thoas/go-funk#DropFloat32 -.. _DropFloat64: https://godoc.org/github.com/thoas/go-funk#DropFloat64 -.. _DropString: https://godoc.org/github.com/thoas/go-funk#DropString - -funk.Initial -............ - -Gets all but the last element of array. - -.. code-block:: go - - funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3} - -funk.Tail -......... - -Gets all but the first element of array. - -.. code-block:: go - - funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4} - -funk.Shuffle -............ - -Creates an array of shuffled values. - -.. code-block:: go - - funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0} - - -see also, typesafe implementations: ShuffleInt_, ShuffleInt64_, ShuffleFloat32_, ShuffleFloat64_, ShuffleString_ - -.. _ShuffleFloat32: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat32 -.. _ShuffleFloat64: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat64 -.. _ShuffleInt: https://godoc.org/github.com/thoas/go-funk#ShuffleInt -.. _ShuffleInt64: https://godoc.org/github.com/thoas/go-funk#ShuffleInt64 -.. _ShuffleString: https://godoc.org/github.com/thoas/go-funk#ShuffleString - -funk.Subtract -............. - -Returns the subtraction between two collections. It preserve order. - -.. code-block:: go - - funk.Subtract([]int{0, 1, 2, 3, 4}, []int{0, 4}) // []int{1, 2, 3} - funk.Subtract([]int{0, 3, 2, 3, 4}, []int{0, 4}) // []int{3, 2, 3} - - -see also, typesafe implementations: SubtractString_ - -.. SubtractString: https://godoc.org/github.com/thoas/go-funk#SubtractString - -funk.Sum -........ - -Computes the sum of the values in an array. - -.. code-block:: go - - funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0 - funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5 - -see also, typesafe implementations: SumInt_, SumInt64_, SumFloat32_, SumFloat64_ - -.. _SumFloat32: https://godoc.org/github.com/thoas/go-funk#SumFloat32 -.. _SumFloat64: https://godoc.org/github.com/thoas/go-funk#SumFloat64 -.. _SumInt: https://godoc.org/github.com/thoas/go-funk#SumInt -.. _SumInt64: https://godoc.org/github.com/thoas/go-funk#SumInt64 - -funk.Reverse -............ - -Transforms an array such that the first element will become the last, the second element -will become the second to last, etc. - -.. code-block:: go - - funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0} - -see also, typesafe implementations: ReverseInt_, ReverseInt64_, ReverseFloat32_, ReverseFloat64_, ReverseString_, ReverseStrings_ - -.. _ReverseFloat32: https://godoc.org/github.com/thoas/go-funk#ReverseFloat32 -.. _ReverseFloat64: https://godoc.org/github.com/thoas/go-funk#ReverseFloat64 -.. _ReverseInt: https://godoc.org/github.com/thoas/go-funk#ReverseInt -.. _ReverseInt64: https://godoc.org/github.com/thoas/go-funk#ReverseInt64 -.. _ReverseString: https://godoc.org/github.com/thoas/go-funk#ReverseString -.. _ReverseStrings: https://godoc.org/github.com/thoas/go-funk#ReverseStrings - -funk.SliceOf -............ - -Returns a slice based on an element. - -.. code-block:: go - - funk.SliceOf(f) // will return a []*Foo{f} - -funk.RandomInt -.............. - -Generates a random int, based on a min and max values. - -.. code-block:: go - - funk.RandomInt(0, 100) // will be between 0 and 100 - -funk.RandomString -................. - -Generates a random string with a fixed length. - -.. code-block:: go - - funk.RandomString(4) // will be a string of 4 random characters - -funk.Shard -.......... - -Generates a sharded string with a fixed length and depth. - -.. code-block:: go - - funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"} - - funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"} - - funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 3, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"} - -funk.Subset -............. - -Returns true if a collection is a subset of another - -.. code-block:: go - - funk.Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) // true - funk.Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) //true - - -Performance ------------ - -``go-funk`` currently has an open issue about performance_, don't hesitate to participate in the discussion -to enhance the generic helpers implementations. - -Let's stop beating around the bush, a typesafe implementation in pure Go of ``funk.Contains``, let's say for example: - -.. code-block:: go - - func ContainsInt(s []int, e int) bool { - for _, a := range s { - if a == e { - return true - } - } - return false - } - -will always outperform an implementation based on reflect_ in terms of speed and allocs because of -how it's implemented in the language. - -If you want a similarity, gorm_ will always be slower than sqlx_ (which is very low level btw) and will use more allocs. - -You must not think generic helpers of ``go-funk`` as a replacement when you are dealing with performance in your codebase, -you should use typesafe implementations instead. - -Contributing ------------- - -* Ping me on twitter `@thoas `_ (DMs, mentions, whatever :)) -* Fork the `project `_ -* Fix `open issues `_ or request new features - -Don't hesitate ;) - -Authors -------- - -* Florent Messa -* Gilles Fabio -* Alexey Pokhozhaev -* Alexandre Nicolaie - -.. _reflect: https://golang.org/pkg/reflect/ -.. _lodash: https://lodash.com/ -.. _performance: https://github.com/thoas/go-funk/issues/19 -.. _gorm: https://github.com/jinzhu/gorm -.. _sqlx: https://github.com/jmoiron/sqlx -.. _godoc: https://godoc.org/github.com/thoas/go-funk diff --git a/vendor/github.com/thoas/go-funk/assign.go b/vendor/github.com/thoas/go-funk/assign.go deleted file mode 100644 index e35c7500006d1..0000000000000 --- a/vendor/github.com/thoas/go-funk/assign.go +++ /dev/null @@ -1,129 +0,0 @@ -package funk - -import ( - "errors" - "fmt" - "reflect" - "strings" -) - -// Set assigns in at path with value val. i.e. in.path = val -// in accepts types of ptr to struct, ptr to variable, slice and ptr to slice. -// Along the path, interface{} is supported and nil ptr is initialized to ptr to zero value -// of the type until the variable to be set is obtained. -// It returns errors when encountering along the path unknown types, uninitialized -// interface{} or interface{} containing struct directly (not ptr to struct). -// -// Slice is resolved the same way in funk.Get(), by traversing each element of the slice, -// so that each element of the slice's corresponding field are going to be set to the same provided val. -// If Set is called on slice with empty path "", it behaves the same as funk.Fill() -// -// If in is well formed, i.e. do not expect above descripted errors to happen, funk.MustSet() -// is a short hand wrapper to discard error return -func Set(in interface{}, val interface{}, path string) error { - if in == nil { - return errors.New("Cannot Set nil") - } - parts := []string{} - if path != "" { - parts = strings.Split(path, ".") - } - return setByParts(in, val, parts) -} - -// we need this layer to handle interface{} type -func setByParts(in interface{}, val interface{}, parts []string) error { - - if in == nil { - // nil interface can happen during traversing the path - return errors.New("Cannot traverse nil/uninitialized interface{}") - } - - inValue := reflect.ValueOf(in) - inKind := inValue.Type().Kind() - - // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. - // I.e. it is not CanAddr() or CanSet() - // So we require in interface{} to be a ptr, slice or array - if inKind == reflect.Ptr { - inValue = inValue.Elem() // if it is ptr we set its content not ptr its self - } else if inKind != reflect.Array && inKind != reflect.Slice { - return fmt.Errorf("Type %s not supported by Set", inValue.Type().String()) - } - - return set(inValue, reflect.ValueOf(val), parts) -} - -// traverse inValue using path in parts and set the dst to be setValue -func set(inValue reflect.Value, setValue reflect.Value, parts []string) error { - - // traverse the path to get the inValue we need to set - i := 0 - for i < len(parts) { - - kind := inValue.Kind() - - switch kind { - case reflect.Invalid: - // do not expect this case to happen - return errors.New("nil pointer found along the path") - case reflect.Struct: - fValue := inValue.FieldByName(parts[i]) - if !fValue.IsValid() { - return fmt.Errorf("field name %v is not found in struct %v", parts[i], inValue.Type().String()) - } - if !fValue.CanSet() { - return fmt.Errorf("field name %v is not exported in struct %v", parts[i], inValue.Type().String()) - } - inValue = fValue - i++ - case reflect.Slice | reflect.Array: - // set all its elements - length := inValue.Len() - for j := 0; j < length; j++ { - err := set(inValue.Index(j), setValue, parts[i:]) - if err != nil { - return err - } - } - return nil - case reflect.Ptr: - // only traverse down one level - if inValue.IsNil() { - // we initialize nil ptr to ptr to zero value of the type - // and continue traversing - inValue.Set(reflect.New(inValue.Type().Elem())) - } - // traverse the ptr until it is not pointer any more or is nil again - inValue = redirectValue(inValue) - case reflect.Interface: - // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. - // I.e. it is not CanAddr() or CanSet(). This is why setByParts has a nil ptr check. - // we treat this as a new call to setByParts, and it will do proper check of the types - return setByParts(inValue.Interface(), setValue.Interface(), parts[i:]) - default: - return fmt.Errorf("kind %v in path %v is not supported", kind, parts[i]) - } - - } - // here inValue holds the value we need to set - - // interface{} can be set to any val - // other types we ensure the type matches - if inValue.Kind() != setValue.Kind() && inValue.Kind() != reflect.Interface { - return fmt.Errorf("cannot set target of type %v with type %v", inValue.Kind(), setValue.Kind()) - } - inValue.Set(setValue) - - return nil -} - -// MustSet is functionally the same as Set. -// It panics instead of returning error. -// It is safe to use if the in value is well formed. -func MustSet(in interface{}, val interface{}, path string) { - err := Set(in, val, path) - if err != nil { - panic(err) - } -} diff --git a/vendor/github.com/thoas/go-funk/builder.go b/vendor/github.com/thoas/go-funk/builder.go deleted file mode 100644 index 6dfc814d73241..0000000000000 --- a/vendor/github.com/thoas/go-funk/builder.go +++ /dev/null @@ -1,110 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -// Builder contains all tools which can be chained. -type Builder interface { - Chunk(size int) Builder - Compact() Builder - Drop(n int) Builder - Filter(predicate interface{}) Builder - Flatten() Builder - FlattenDeep() Builder - Initial() Builder - Intersect(y interface{}) Builder - Join(rarr interface{}, fnc JoinFnc) Builder - Map(mapFunc interface{}) Builder - FlatMap(mapFunc interface{}) Builder - Reverse() Builder - Shuffle() Builder - Tail() Builder - Uniq() Builder - Without(values ...interface{}) Builder - - All() bool - Any() bool - Contains(elem interface{}) bool - Every(elements ...interface{}) bool - Find(predicate interface{}) interface{} - ForEach(predicate interface{}) - ForEachRight(predicate interface{}) - Head() interface{} - Keys() interface{} - IndexOf(elem interface{}) int - IsEmpty() bool - Last() interface{} - LastIndexOf(elem interface{}) int - NotEmpty() bool - Product() float64 - Reduce(reduceFunc, acc interface{}) interface{} - Sum() float64 - Type() reflect.Type - Value() interface{} - Values() interface{} -} - -// Chain creates a simple new go-funk.Builder from a collection. Each method -// call generate a new builder containing the previous result. -func Chain(v interface{}) Builder { - isNotNil(v, "Chain") - - valueType := reflect.TypeOf(v) - if isValidBuilderEntry(valueType) || - (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { - return &chainBuilder{v} - } - - panic(fmt.Sprintf("Type %s is not supported by Chain", valueType.String())) -} - -// LazyChain creates a lazy go-funk.Builder from a collection. Each method call -// generate a new builder containing a method generating the previous value. -// With that, all data are only generated when we call a tailling method like All or Find. -func LazyChain(v interface{}) Builder { - isNotNil(v, "LazyChain") - - valueType := reflect.TypeOf(v) - if isValidBuilderEntry(valueType) || - (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { - return &lazyBuilder{func() interface{} { return v }} - } - - panic(fmt.Sprintf("Type %s is not supported by LazyChain", valueType.String())) - -} - -// LazyChainWith creates a lazy go-funk.Builder from a generator. Like LazyChain, each -// method call generate a new builder containing a method generating the previous value. -// But, instead of using a collection, it takes a generator which can generate values. -// With LazyChainWith, to can create a generic pipeline of collection transformation and, -// throw the generator, sending different collection. -func LazyChainWith(generator func() interface{}) Builder { - isNotNil(generator, "LazyChainWith") - return &lazyBuilder{func() interface{} { - isNotNil(generator, "LazyChainWith") - - v := generator() - valueType := reflect.TypeOf(v) - if isValidBuilderEntry(valueType) || - (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { - return v - } - - panic(fmt.Sprintf("Type %s is not supported by LazyChainWith generator", valueType.String())) - }} -} - -func isNotNil(v interface{}, from string) { - if v == nil { - panic(fmt.Sprintf("nil value is not supported by %s", from)) - } -} - -func isValidBuilderEntry(valueType reflect.Type) bool { - return valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array || - valueType.Kind() == reflect.Map || - valueType.Kind() == reflect.String -} diff --git a/vendor/github.com/thoas/go-funk/chain_builder.go b/vendor/github.com/thoas/go-funk/chain_builder.go deleted file mode 100644 index 18226ab573c51..0000000000000 --- a/vendor/github.com/thoas/go-funk/chain_builder.go +++ /dev/null @@ -1,142 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -type chainBuilder struct { - collection interface{} -} - -func (b *chainBuilder) Chunk(size int) Builder { - return &chainBuilder{Chunk(b.collection, size)} -} -func (b *chainBuilder) Compact() Builder { - return &chainBuilder{Compact(b.collection)} -} -func (b *chainBuilder) Drop(n int) Builder { - return &chainBuilder{Drop(b.collection, n)} -} -func (b *chainBuilder) Filter(predicate interface{}) Builder { - return &chainBuilder{Filter(b.collection, predicate)} -} -func (b *chainBuilder) Flatten() Builder { - return &chainBuilder{Flatten(b.collection)} -} -func (b *chainBuilder) FlattenDeep() Builder { - return &chainBuilder{FlattenDeep(b.collection)} -} -func (b *chainBuilder) Initial() Builder { - return &chainBuilder{Initial(b.collection)} -} -func (b *chainBuilder) Intersect(y interface{}) Builder { - return &chainBuilder{Intersect(b.collection, y)} -} -func (b *chainBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { - return &chainBuilder{Join(b.collection, rarr, fnc)} -} -func (b *chainBuilder) Map(mapFunc interface{}) Builder { - return &chainBuilder{Map(b.collection, mapFunc)} -} -func (b *chainBuilder) FlatMap(mapFunc interface{}) Builder { - return &chainBuilder{FlatMap(b.collection, mapFunc)} -} -func (b *chainBuilder) Reverse() Builder { - return &chainBuilder{Reverse(b.collection)} -} -func (b *chainBuilder) Shuffle() Builder { - return &chainBuilder{Shuffle(b.collection)} -} -func (b *chainBuilder) Tail() Builder { - return &chainBuilder{Tail(b.collection)} -} -func (b *chainBuilder) Uniq() Builder { - return &chainBuilder{Uniq(b.collection)} -} -func (b *chainBuilder) Without(values ...interface{}) Builder { - return &chainBuilder{Without(b.collection, values...)} -} - -func (b *chainBuilder) All() bool { - v := reflect.ValueOf(b.collection) - t := v.Type() - - if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { - panic(fmt.Sprintf("Type %s is not supported by Chain.All", t.String())) - } - - c := make([]interface{}, v.Len()) - for i := 0; i < v.Len(); i++ { - c[i] = v.Index(i).Interface() - } - return All(c...) -} -func (b *chainBuilder) Any() bool { - v := reflect.ValueOf(b.collection) - t := v.Type() - - if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { - panic(fmt.Sprintf("Type %s is not supported by Chain.Any", t.String())) - } - - c := make([]interface{}, v.Len()) - for i := 0; i < v.Len(); i++ { - c[i] = v.Index(i).Interface() - } - return Any(c...) -} -func (b *chainBuilder) Contains(elem interface{}) bool { - return Contains(b.collection, elem) -} -func (b *chainBuilder) Every(elements ...interface{}) bool { - return Every(b.collection, elements...) -} -func (b *chainBuilder) Find(predicate interface{}) interface{} { - return Find(b.collection, predicate) -} -func (b *chainBuilder) ForEach(predicate interface{}) { - ForEach(b.collection, predicate) -} -func (b *chainBuilder) ForEachRight(predicate interface{}) { - ForEachRight(b.collection, predicate) -} -func (b *chainBuilder) Head() interface{} { - return Head(b.collection) -} -func (b *chainBuilder) Keys() interface{} { - return Keys(b.collection) -} -func (b *chainBuilder) IndexOf(elem interface{}) int { - return IndexOf(b.collection, elem) -} -func (b *chainBuilder) IsEmpty() bool { - return IsEmpty(b.collection) -} -func (b *chainBuilder) Last() interface{} { - return Last(b.collection) -} -func (b *chainBuilder) LastIndexOf(elem interface{}) int { - return LastIndexOf(b.collection, elem) -} -func (b *chainBuilder) NotEmpty() bool { - return NotEmpty(b.collection) -} -func (b *chainBuilder) Product() float64 { - return Product(b.collection) -} -func (b *chainBuilder) Reduce(reduceFunc, acc interface{}) interface{} { - return Reduce(b.collection, reduceFunc, acc) -} -func (b *chainBuilder) Sum() float64 { - return Sum(b.collection) -} -func (b *chainBuilder) Type() reflect.Type { - return reflect.TypeOf(b.collection) -} -func (b *chainBuilder) Value() interface{} { - return b.collection -} -func (b *chainBuilder) Values() interface{} { - return Values(b.collection) -} diff --git a/vendor/github.com/thoas/go-funk/compact.go b/vendor/github.com/thoas/go-funk/compact.go deleted file mode 100644 index ba151aa9c25c9..0000000000000 --- a/vendor/github.com/thoas/go-funk/compact.go +++ /dev/null @@ -1,50 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Compact creates a slice with all empty/zero values removed. -func Compact(value interface{}) interface{} { - arr := redirectValue(reflect.ValueOf(value)) - - if arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { - panic("First parameter must be array or slice") - } - - sliceElemType := sliceElem(arr.Type()) - resultSliceType := reflect.SliceOf(sliceElemType) - result := reflect.MakeSlice(resultSliceType, 0, 0) - - for i := 0; i < arr.Len(); i++ { - elemVal := arr.Index(i) - - if elemVal.Kind() == reflect.Interface { - elemVal = elemVal.Elem() - } - - redirectedElemVal := redirectValue(elemVal) - - switch redirectedElemVal.Kind() { - case reflect.Invalid: - continue - case reflect.Func: - if redirectedElemVal.IsNil() { - continue - } - case reflect.Map, reflect.Slice, reflect.Chan: - if redirectedElemVal.Len() == 0 { - continue - } - default: - defaultValue := reflect.Zero(redirectedElemVal.Type()).Interface() - if redirectedElemVal.Interface() == defaultValue { - continue - } - } - - result = reflect.Append(result, elemVal) - } - - return result.Interface() -} diff --git a/vendor/github.com/thoas/go-funk/fill.go b/vendor/github.com/thoas/go-funk/fill.go deleted file mode 100644 index 6af5cfa77fc93..0000000000000 --- a/vendor/github.com/thoas/go-funk/fill.go +++ /dev/null @@ -1,34 +0,0 @@ -package funk - -import ( - "errors" - "fmt" - "reflect" -) - -// Fill fills elements of array with value -func Fill(in interface{}, fillValue interface{}) (interface{}, error) { - inValue := reflect.ValueOf(in) - inKind := inValue.Type().Kind() - if inKind != reflect.Slice && inKind != reflect.Array { - return nil, errors.New("Can only fill slices and arrays") - } - - inType := reflect.TypeOf(in).Elem() - value := reflect.ValueOf(fillValue) - if inType != value.Type() { - return nil, fmt.Errorf( - "Cannot fill '%s' with '%s'", reflect.TypeOf(in), value.Type(), - ) - } - - length := inValue.Len() - newSlice := reflect.SliceOf(reflect.TypeOf(fillValue)) - in = reflect.MakeSlice(newSlice, length, length).Interface() - inValue = reflect.ValueOf(in) - - for i := 0; i < length; i++ { - inValue.Index(i).Set(value) - } - return in, nil -} diff --git a/vendor/github.com/thoas/go-funk/go.mod b/vendor/github.com/thoas/go-funk/go.mod deleted file mode 100644 index 23fb24af3e95f..0000000000000 --- a/vendor/github.com/thoas/go-funk/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/thoas/go-funk - -go 1.13 - -require github.com/stretchr/testify v1.4.0 diff --git a/vendor/github.com/thoas/go-funk/go.sum b/vendor/github.com/thoas/go-funk/go.sum deleted file mode 100644 index 8fdee5854f19a..0000000000000 --- a/vendor/github.com/thoas/go-funk/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/thoas/go-funk/helpers.go b/vendor/github.com/thoas/go-funk/helpers.go deleted file mode 100644 index da3f032a5d437..0000000000000 --- a/vendor/github.com/thoas/go-funk/helpers.go +++ /dev/null @@ -1,317 +0,0 @@ -package funk - -import ( - "bytes" - "math/rand" - "reflect" -) - -var numericZeros = []interface{}{ - int(0), - int8(0), - int16(0), - int32(0), - int64(0), - uint(0), - uint8(0), - uint16(0), - uint32(0), - uint64(0), - float32(0), - float64(0), -} - -// ToFloat64 converts any numeric value to float64. -func ToFloat64(x interface{}) (float64, bool) { - var xf float64 - xok := true - - switch xn := x.(type) { - case uint8: - xf = float64(xn) - case uint16: - xf = float64(xn) - case uint32: - xf = float64(xn) - case uint64: - xf = float64(xn) - case int: - xf = float64(xn) - case int8: - xf = float64(xn) - case int16: - xf = float64(xn) - case int32: - xf = float64(xn) - case int64: - xf = float64(xn) - case float32: - xf = float64(xn) - case float64: - xf = float64(xn) - default: - xok = false - } - - return xf, xok -} - -// PtrOf makes a copy of the given interface and returns a pointer. -func PtrOf(itf interface{}) interface{} { - t := reflect.TypeOf(itf) - - cp := reflect.New(t) - cp.Elem().Set(reflect.ValueOf(itf)) - - // Avoid double pointers if itf is a pointer - if t.Kind() == reflect.Ptr { - return cp.Elem().Interface() - } - - return cp.Interface() -} - -// IsFunction returns if the argument is a function. -func IsFunction(in interface{}, num ...int) bool { - funcType := reflect.TypeOf(in) - - result := funcType != nil && funcType.Kind() == reflect.Func - - if len(num) >= 1 { - result = result && funcType.NumIn() == num[0] - } - - if len(num) == 2 { - result = result && funcType.NumOut() == num[1] - } - - return result -} - -// IsPredicate returns if the argument is a predicate function. -func IsPredicate(in interface{}, inTypes ...reflect.Type) bool { - if len(inTypes) == 0 { - inTypes = append(inTypes, nil) - } - - funcType := reflect.TypeOf(in) - - result := funcType != nil && funcType.Kind() == reflect.Func - - result = result && funcType.NumOut() == 1 && funcType.Out(0).Kind() == reflect.Bool - result = result && funcType.NumIn() == len(inTypes) - - for i := 0; result && i < len(inTypes); i++ { - inType := inTypes[i] - result = inType == nil || inType.ConvertibleTo(funcType.In(i)) - } - - return result -} - -// IsEqual returns if the two objects are equal -func IsEqual(expected interface{}, actual interface{}) bool { - if expected == nil || actual == nil { - return expected == actual - } - - if exp, ok := expected.([]byte); ok { - act, ok := actual.([]byte) - if !ok { - return false - } - - if exp == nil || act == nil { - return true - } - - return bytes.Equal(exp, act) - } - - return reflect.DeepEqual(expected, actual) - -} - -// IsType returns if the two objects are in the same type -func IsType(expected interface{}, actual interface{}) bool { - return IsEqual(reflect.TypeOf(expected), reflect.TypeOf(actual)) -} - -// Equal returns if the two objects are equal -func Equal(expected interface{}, actual interface{}) bool { - return IsEqual(expected, actual) -} - -// NotEqual returns if the two objects are not equal -func NotEqual(expected interface{}, actual interface{}) bool { - return !IsEqual(expected, actual) -} - -// IsIteratee returns if the argument is an iteratee. -func IsIteratee(in interface{}) bool { - if in == nil { - return false - } - arrType := reflect.TypeOf(in) - - kind := arrType.Kind() - - return kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map -} - -// IsCollection returns if the argument is a collection. -func IsCollection(in interface{}) bool { - arrType := reflect.TypeOf(in) - - kind := arrType.Kind() - - return kind == reflect.Array || kind == reflect.Slice -} - -// SliceOf returns a slice which contains the element. -func SliceOf(in interface{}) interface{} { - value := reflect.ValueOf(in) - - sliceType := reflect.SliceOf(reflect.TypeOf(in)) - slice := reflect.New(sliceType) - sliceValue := reflect.MakeSlice(sliceType, 0, 0) - sliceValue = reflect.Append(sliceValue, value) - slice.Elem().Set(sliceValue) - - return slice.Elem().Interface() -} - -// Any returns true if any element of the iterable is not empty. If the iterable is empty, return False. -func Any(objs ...interface{}) bool { - if len(objs) == 0 { - return false - } - - for _, obj := range objs { - if !IsEmpty(obj) { - return true - } - } - - return false -} - -// All returns true if all elements of the iterable are not empty (or if the iterable is empty) -func All(objs ...interface{}) bool { - if len(objs) == 0 { - return true - } - - for _, obj := range objs { - if IsEmpty(obj) { - return false - } - } - - return true -} - -// IsEmpty returns if the object is considered as empty or not. -func IsEmpty(obj interface{}) bool { - if obj == nil || obj == "" || obj == false { - return true - } - - for _, v := range numericZeros { - if obj == v { - return true - } - } - - objValue := reflect.ValueOf(obj) - - switch objValue.Kind() { - case reflect.Map: - fallthrough - case reflect.Slice, reflect.Chan: - return objValue.Len() == 0 - case reflect.Struct: - return reflect.DeepEqual(obj, ZeroOf(obj)) - case reflect.Ptr: - if objValue.IsNil() { - return true - } - - obj = redirectValue(objValue).Interface() - - return reflect.DeepEqual(obj, ZeroOf(obj)) - } - - return false -} - -// IsZero returns if the object is considered as zero value -func IsZero(obj interface{}) bool { - if obj == nil || obj == "" || obj == false { - return true - } - - for _, v := range numericZeros { - if obj == v { - return true - } - } - - return reflect.DeepEqual(obj, ZeroOf(obj)) -} - -// NotEmpty returns if the object is considered as non-empty or not. -func NotEmpty(obj interface{}) bool { - return !IsEmpty(obj) -} - -// ZeroOf returns a zero value of an element. -func ZeroOf(in interface{}) interface{} { - if in == nil { - return nil - } - - return reflect.Zero(reflect.TypeOf(in)).Interface() -} - -// RandomInt generates a random int, based on a min and max values -func RandomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// Shard will shard a string name -func Shard(str string, width int, depth int, restOnly bool) []string { - var results []string - - for i := 0; i < depth; i++ { - results = append(results, str[(width*i):(width*(i+1))]) - } - - if restOnly { - results = append(results, str[(width*depth):]) - } else { - results = append(results, str) - } - - return results -} - -var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - -// RandomString returns a random string with a fixed length -func RandomString(n int, allowedChars ...[]rune) string { - var letters []rune - - if len(allowedChars) == 0 { - letters = defaultLetters - } else { - letters = allowedChars[0] - } - - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - - return string(b) -} diff --git a/vendor/github.com/thoas/go-funk/intersection.go b/vendor/github.com/thoas/go-funk/intersection.go deleted file mode 100644 index 17ec69e3ba6c6..0000000000000 --- a/vendor/github.com/thoas/go-funk/intersection.go +++ /dev/null @@ -1,253 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Intersect returns the intersection between two collections. -// -// Deprecated: use Join(x, y, InnerJoin) instead of Intersect, InnerJoin -// implements deduplication mechanism, so verify your code behaviour -// before using it -func Intersect(x interface{}, y interface{}) interface{} { - if !IsCollection(x) { - panic("First parameter must be a collection") - } - if !IsCollection(y) { - panic("Second parameter must be a collection") - } - - hash := map[interface{}]struct{}{} - - xValue := reflect.ValueOf(x) - xType := xValue.Type() - - yValue := reflect.ValueOf(y) - yType := yValue.Type() - - if NotEqual(xType, yType) { - panic("Parameters must have the same type") - } - - zType := reflect.SliceOf(xType.Elem()) - zSlice := reflect.MakeSlice(zType, 0, 0) - - for i := 0; i < xValue.Len(); i++ { - v := xValue.Index(i).Interface() - hash[v] = struct{}{} - } - - for i := 0; i < yValue.Len(); i++ { - v := yValue.Index(i).Interface() - _, ok := hash[v] - if ok { - zSlice = reflect.Append(zSlice, yValue.Index(i)) - } - } - - return zSlice.Interface() -} - -// IntersectString returns the intersection between two collections of string. -func IntersectString(x []string, y []string) []string { - if len(x) == 0 || len(y) == 0 { - return []string{} - } - - set := []string{} - hash := map[string]struct{}{} - - for _, v := range x { - hash[v] = struct{}{} - } - - for _, v := range y { - _, ok := hash[v] - if ok { - set = append(set, v) - } - } - - return set -} - -// Difference returns the difference between two collections. -func Difference(x interface{}, y interface{}) (interface{}, interface{}) { - if !IsCollection(x) { - panic("First parameter must be a collection") - } - if !IsCollection(y) { - panic("Second parameter must be a collection") - } - - xValue := reflect.ValueOf(x) - xType := xValue.Type() - - yValue := reflect.ValueOf(y) - yType := yValue.Type() - - if NotEqual(xType, yType) { - panic("Parameters must have the same type") - } - - leftType := reflect.SliceOf(xType.Elem()) - leftSlice := reflect.MakeSlice(leftType, 0, 0) - rightType := reflect.SliceOf(yType.Elem()) - rightSlice := reflect.MakeSlice(rightType, 0, 0) - - for i := 0; i < xValue.Len(); i++ { - v := xValue.Index(i).Interface() - if Contains(y, v) == false { - leftSlice = reflect.Append(leftSlice, xValue.Index(i)) - } - } - - for i := 0; i < yValue.Len(); i++ { - v := yValue.Index(i).Interface() - if Contains(x, v) == false { - rightSlice = reflect.Append(rightSlice, yValue.Index(i)) - } - } - - return leftSlice.Interface(), rightSlice.Interface() -} - -// DifferenceString returns the difference between two collections of strings. -func DifferenceString(x []string, y []string) ([]string, []string) { - leftSlice := []string{} - rightSlice := []string{} - - for _, v := range x { - if ContainsString(y, v) == false { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if ContainsString(x, v) == false { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceInt64 returns the difference between two collections of int64s. -func DifferenceInt64(x []int64, y []int64) ([]int64, []int64) { - leftSlice := []int64{} - rightSlice := []int64{} - - for _, v := range x { - if ContainsInt64(y, v) == false { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if ContainsInt64(x, v) == false { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceInt32 returns the difference between two collections of ints32. -func DifferenceInt32(x []int32, y []int32) ([]int32, []int32) { - leftSlice := []int32{} - rightSlice := []int32{} - - for _, v := range x { - if !ContainsInt32(y, v) { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if !ContainsInt32(x, v) { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceInt returns the difference between two collections of ints. -func DifferenceInt(x []int, y []int) ([]int, []int) { - leftSlice := []int{} - rightSlice := []int{} - - for _, v := range x { - if ContainsInt(y, v) == false { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if ContainsInt(x, v) == false { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceUInt returns the difference between two collections of uints. -func DifferenceUInt(x []uint, y []uint) ([]uint, []uint) { - leftSlice := []uint{} - rightSlice := []uint{} - - for _, v := range x { - if !ContainsUInt(y, v) { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if !ContainsUInt(x, v) { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceUInt32 returns the difference between two collections of uints32. -func DifferenceUInt32(x []uint32, y []uint32) ([]uint32, []uint32) { - leftSlice := []uint32{} - rightSlice := []uint32{} - - for _, v := range x { - if !ContainsUInt32(y, v) { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if !ContainsUInt32(x, v) { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} - -// DifferenceUInt64 returns the difference between two collections of uints64. -func DifferenceUInt64(x []uint64, y []uint64) ([]uint64, []uint64) { - leftSlice := []uint64{} - rightSlice := []uint64{} - - for _, v := range x { - if !ContainsUInt64(y, v) { - leftSlice = append(leftSlice, v) - } - } - - for _, v := range y { - if !ContainsUInt64(x, v) { - rightSlice = append(rightSlice, v) - } - } - - return leftSlice, rightSlice -} diff --git a/vendor/github.com/thoas/go-funk/join.go b/vendor/github.com/thoas/go-funk/join.go deleted file mode 100644 index 6d0e287777de4..0000000000000 --- a/vendor/github.com/thoas/go-funk/join.go +++ /dev/null @@ -1,86 +0,0 @@ -package funk - -import ( - "reflect" -) - -type JoinFnc func(lx, rx reflect.Value) reflect.Value - -// Join combines two collections using the given join method. -func Join(larr, rarr interface{}, fnc JoinFnc) interface{} { - if !IsCollection(larr) { - panic("First parameter must be a collection") - } - if !IsCollection(rarr) { - panic("Second parameter must be a collection") - } - - lvalue := reflect.ValueOf(larr) - rvalue := reflect.ValueOf(rarr) - if NotEqual(lvalue.Type(), rvalue.Type()) { - panic("Parameters must have the same type") - } - - return fnc(lvalue, rvalue).Interface() -} - -// InnerJoin finds and returns matching data from two collections. -func InnerJoin(lx, rx reflect.Value) reflect.Value { - result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()+rx.Len()) - rhash := hashSlice(rx) - lhash := make(map[interface{}]struct{}, lx.Len()) - - for i := 0; i < lx.Len(); i++ { - v := lx.Index(i) - _, ok := rhash[v.Interface()] - _, alreadyExists := lhash[v.Interface()] - if ok && !alreadyExists { - lhash[v.Interface()] = struct{}{} - result = reflect.Append(result, v) - } - } - return result -} - -// OuterJoin finds and returns dissimilar data from two collections. -func OuterJoin(lx, rx reflect.Value) reflect.Value { - ljoin := LeftJoin(lx, rx) - rjoin := RightJoin(lx, rx) - - result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), ljoin.Len()+rjoin.Len(), ljoin.Len()+rjoin.Len()) - for i := 0; i < ljoin.Len(); i++ { - result.Index(i).Set(ljoin.Index(i)) - } - for i := 0; i < rjoin.Len(); i++ { - result.Index(ljoin.Len() + i).Set(rjoin.Index(i)) - } - - return result -} - -// LeftJoin finds and returns dissimilar data from the first collection (left). -func LeftJoin(lx, rx reflect.Value) reflect.Value { - result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()) - rhash := hashSlice(rx) - - for i := 0; i < lx.Len(); i++ { - v := lx.Index(i) - _, ok := rhash[v.Interface()] - if !ok { - result = reflect.Append(result, v) - } - } - return result -} - -// LeftJoin finds and returns dissimilar data from the second collection (right). -func RightJoin(lx, rx reflect.Value) reflect.Value { return LeftJoin(rx, lx) } - -func hashSlice(arr reflect.Value) map[interface{}]struct{} { - hash := map[interface{}]struct{}{} - for i := 0; i < arr.Len(); i++ { - v := arr.Index(i).Interface() - hash[v] = struct{}{} - } - return hash -} diff --git a/vendor/github.com/thoas/go-funk/join_primitives.go b/vendor/github.com/thoas/go-funk/join_primitives.go deleted file mode 100644 index 1e449833ba4d1..0000000000000 --- a/vendor/github.com/thoas/go-funk/join_primitives.go +++ /dev/null @@ -1,385 +0,0 @@ -package funk - -type JoinIntFnc func(lx, rx []int) []int - -// JoinInt combines two int collections using the given join method. -func JoinInt(larr, rarr []int, fnc JoinIntFnc) []int { - return fnc(larr, rarr) -} - -// InnerJoinInt finds and returns matching data from two int collections. -func InnerJoinInt(lx, rx []int) []int { - result := make([]int, 0, len(lx)+len(rx)) - rhash := hashSliceInt(rx) - lhash := make(map[int]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinInt finds and returns dissimilar data from two int collections. -func OuterJoinInt(lx, rx []int) []int { - ljoin := LeftJoinInt(lx, rx) - rjoin := RightJoinInt(lx, rx) - - result := make([]int, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinInt finds and returns dissimilar data from the first int collection (left). -func LeftJoinInt(lx, rx []int) []int { - result := make([]int, 0, len(lx)) - rhash := hashSliceInt(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinInt finds and returns dissimilar data from the second int collection (right). -func RightJoinInt(lx, rx []int) []int { return LeftJoinInt(rx, lx) } - -func hashSliceInt(arr []int) map[int]struct{} { - hash := make(map[int]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} - -type JoinInt32Fnc func(lx, rx []int32) []int32 - -// JoinInt32 combines two int32 collections using the given join method. -func JoinInt32(larr, rarr []int32, fnc JoinInt32Fnc) []int32 { - return fnc(larr, rarr) -} - -// InnerJoinInt32 finds and returns matching data from two int32 collections. -func InnerJoinInt32(lx, rx []int32) []int32 { - result := make([]int32, 0, len(lx)+len(rx)) - rhash := hashSliceInt32(rx) - lhash := make(map[int32]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinInt32 finds and returns dissimilar data from two int32 collections. -func OuterJoinInt32(lx, rx []int32) []int32 { - ljoin := LeftJoinInt32(lx, rx) - rjoin := RightJoinInt32(lx, rx) - - result := make([]int32, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinInt32 finds and returns dissimilar data from the first int32 collection (left). -func LeftJoinInt32(lx, rx []int32) []int32 { - result := make([]int32, 0, len(lx)) - rhash := hashSliceInt32(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinInt32 finds and returns dissimilar data from the second int32 collection (right). -func RightJoinInt32(lx, rx []int32) []int32 { return LeftJoinInt32(rx, lx) } - -func hashSliceInt32(arr []int32) map[int32]struct{} { - hash := make(map[int32]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} - -type JoinInt64Fnc func(lx, rx []int64) []int64 - -// JoinInt64 combines two int64 collections using the given join method. -func JoinInt64(larr, rarr []int64, fnc JoinInt64Fnc) []int64 { - return fnc(larr, rarr) -} - -// InnerJoinInt64 finds and returns matching data from two int64 collections. -func InnerJoinInt64(lx, rx []int64) []int64 { - result := make([]int64, 0, len(lx)+len(rx)) - rhash := hashSliceInt64(rx) - lhash := make(map[int64]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinInt64 finds and returns dissimilar data from two int64 collections. -func OuterJoinInt64(lx, rx []int64) []int64 { - ljoin := LeftJoinInt64(lx, rx) - rjoin := RightJoinInt64(lx, rx) - - result := make([]int64, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinInt64 finds and returns dissimilar data from the first int64 collection (left). -func LeftJoinInt64(lx, rx []int64) []int64 { - result := make([]int64, 0, len(lx)) - rhash := hashSliceInt64(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinInt64 finds and returns dissimilar data from the second int64 collection (right). -func RightJoinInt64(lx, rx []int64) []int64 { return LeftJoinInt64(rx, lx) } - -func hashSliceInt64(arr []int64) map[int64]struct{} { - hash := make(map[int64]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} - -type JoinStringFnc func(lx, rx []string) []string - -// JoinString combines two string collections using the given join method. -func JoinString(larr, rarr []string, fnc JoinStringFnc) []string { - return fnc(larr, rarr) -} - -// InnerJoinString finds and returns matching data from two string collections. -func InnerJoinString(lx, rx []string) []string { - result := make([]string, 0, len(lx)+len(rx)) - rhash := hashSliceString(rx) - lhash := make(map[string]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinString finds and returns dissimilar data from two string collections. -func OuterJoinString(lx, rx []string) []string { - ljoin := LeftJoinString(lx, rx) - rjoin := RightJoinString(lx, rx) - - result := make([]string, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinString finds and returns dissimilar data from the first string collection (left). -func LeftJoinString(lx, rx []string) []string { - result := make([]string, 0, len(lx)) - rhash := hashSliceString(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinString finds and returns dissimilar data from the second string collection (right). -func RightJoinString(lx, rx []string) []string { return LeftJoinString(rx, lx) } - -func hashSliceString(arr []string) map[string]struct{} { - hash := make(map[string]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} - -type JoinFloat32Fnc func(lx, rx []float32) []float32 - -// JoinFloat32 combines two float32 collections using the given join method. -func JoinFloat32(larr, rarr []float32, fnc JoinFloat32Fnc) []float32 { - return fnc(larr, rarr) -} - -// InnerJoinFloat32 finds and returns matching data from two float32 collections. -func InnerJoinFloat32(lx, rx []float32) []float32 { - result := make([]float32, 0, len(lx)+len(rx)) - rhash := hashSliceFloat32(rx) - lhash := make(map[float32]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinFloat32 finds and returns dissimilar data from two float32 collections. -func OuterJoinFloat32(lx, rx []float32) []float32 { - ljoin := LeftJoinFloat32(lx, rx) - rjoin := RightJoinFloat32(lx, rx) - - result := make([]float32, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinFloat32 finds and returns dissimilar data from the first float32 collection (left). -func LeftJoinFloat32(lx, rx []float32) []float32 { - result := make([]float32, 0, len(lx)) - rhash := hashSliceFloat32(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinFloat32 finds and returns dissimilar data from the second float32 collection (right). -func RightJoinFloat32(lx, rx []float32) []float32 { return LeftJoinFloat32(rx, lx) } - -func hashSliceFloat32(arr []float32) map[float32]struct{} { - hash := make(map[float32]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} - -type JoinFloat64Fnc func(lx, rx []float64) []float64 - -// JoinFloat64 combines two float64 collections using the given join method. -func JoinFloat64(larr, rarr []float64, fnc JoinFloat64Fnc) []float64 { - return fnc(larr, rarr) -} - -// InnerJoinFloat64 finds and returns matching data from two float64 collections. -func InnerJoinFloat64(lx, rx []float64) []float64 { - result := make([]float64, 0, len(lx)+len(rx)) - rhash := hashSliceFloat64(rx) - lhash := make(map[float64]struct{}, len(lx)) - - for _, v := range lx { - _, ok := rhash[v] - _, alreadyExists := lhash[v] - if ok && !alreadyExists { - lhash[v] = struct{}{} - result = append(result, v) - } - } - return result -} - -// OuterJoinFloat64 finds and returns dissimilar data from two float64 collections. -func OuterJoinFloat64(lx, rx []float64) []float64 { - ljoin := LeftJoinFloat64(lx, rx) - rjoin := RightJoinFloat64(lx, rx) - - result := make([]float64, len(ljoin)+len(rjoin)) - for i, v := range ljoin { - result[i] = v - } - for i, v := range rjoin { - result[len(ljoin)+i] = v - } - return result -} - -// LeftJoinFloat64 finds and returns dissimilar data from the first float64 collection (left). -func LeftJoinFloat64(lx, rx []float64) []float64 { - result := make([]float64, 0, len(lx)) - rhash := hashSliceFloat64(rx) - - for _, v := range lx { - _, ok := rhash[v] - if !ok { - result = append(result, v) - } - } - return result -} - -// LeftJoinFloat64 finds and returns dissimilar data from the second float64 collection (right). -func RightJoinFloat64(lx, rx []float64) []float64 { return LeftJoinFloat64(rx, lx) } - -func hashSliceFloat64(arr []float64) map[float64]struct{} { - hash := make(map[float64]struct{}, len(arr)) - for _, i := range arr { - hash[i] = struct{}{} - } - return hash -} diff --git a/vendor/github.com/thoas/go-funk/lazy_builder.go b/vendor/github.com/thoas/go-funk/lazy_builder.go deleted file mode 100644 index 9ba1907ea108a..0000000000000 --- a/vendor/github.com/thoas/go-funk/lazy_builder.go +++ /dev/null @@ -1,117 +0,0 @@ -package funk - -import "reflect" - -type lazyBuilder struct { - exec func() interface{} -} - -func (b *lazyBuilder) Chunk(size int) Builder { - return &lazyBuilder{func() interface{} { return Chunk(b.exec(), size) }} -} -func (b *lazyBuilder) Compact() Builder { - return &lazyBuilder{func() interface{} { return Compact(b.exec()) }} -} -func (b *lazyBuilder) Drop(n int) Builder { - return &lazyBuilder{func() interface{} { return Drop(b.exec(), n) }} -} -func (b *lazyBuilder) Filter(predicate interface{}) Builder { - return &lazyBuilder{func() interface{} { return Filter(b.exec(), predicate) }} -} -func (b *lazyBuilder) Flatten() Builder { - return &lazyBuilder{func() interface{} { return Flatten(b.exec()) }} -} -func (b *lazyBuilder) FlattenDeep() Builder { - return &lazyBuilder{func() interface{} { return FlattenDeep(b.exec()) }} -} -func (b *lazyBuilder) Initial() Builder { - return &lazyBuilder{func() interface{} { return Initial(b.exec()) }} -} -func (b *lazyBuilder) Intersect(y interface{}) Builder { - return &lazyBuilder{func() interface{} { return Intersect(b.exec(), y) }} -} -func (b *lazyBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { - return &lazyBuilder{func() interface{} { return Join(b.exec(), rarr, fnc) }} -} -func (b *lazyBuilder) Map(mapFunc interface{}) Builder { - return &lazyBuilder{func() interface{} { return Map(b.exec(), mapFunc) }} -} -func (b *lazyBuilder) FlatMap(mapFunc interface{}) Builder { - return &lazyBuilder{func() interface{} { return FlatMap(b.exec(), mapFunc) }} -} -func (b *lazyBuilder) Reverse() Builder { - return &lazyBuilder{func() interface{} { return Reverse(b.exec()) }} -} -func (b *lazyBuilder) Shuffle() Builder { - return &lazyBuilder{func() interface{} { return Shuffle(b.exec()) }} -} -func (b *lazyBuilder) Tail() Builder { - return &lazyBuilder{func() interface{} { return Tail(b.exec()) }} -} -func (b *lazyBuilder) Uniq() Builder { - return &lazyBuilder{func() interface{} { return Uniq(b.exec()) }} -} -func (b *lazyBuilder) Without(values ...interface{}) Builder { - return &lazyBuilder{func() interface{} { return Without(b.exec(), values...) }} -} - -func (b *lazyBuilder) All() bool { - return (&chainBuilder{b.exec()}).All() -} -func (b *lazyBuilder) Any() bool { - return (&chainBuilder{b.exec()}).Any() -} -func (b *lazyBuilder) Contains(elem interface{}) bool { - return Contains(b.exec(), elem) -} -func (b *lazyBuilder) Every(elements ...interface{}) bool { - return Every(b.exec(), elements...) -} -func (b *lazyBuilder) Find(predicate interface{}) interface{} { - return Find(b.exec(), predicate) -} -func (b *lazyBuilder) ForEach(predicate interface{}) { - ForEach(b.exec(), predicate) -} -func (b *lazyBuilder) ForEachRight(predicate interface{}) { - ForEachRight(b.exec(), predicate) -} -func (b *lazyBuilder) Head() interface{} { - return Head(b.exec()) -} -func (b *lazyBuilder) Keys() interface{} { - return Keys(b.exec()) -} -func (b *lazyBuilder) IndexOf(elem interface{}) int { - return IndexOf(b.exec(), elem) -} -func (b *lazyBuilder) IsEmpty() bool { - return IsEmpty(b.exec()) -} -func (b *lazyBuilder) Last() interface{} { - return Last(b.exec()) -} -func (b *lazyBuilder) LastIndexOf(elem interface{}) int { - return LastIndexOf(b.exec(), elem) -} -func (b *lazyBuilder) NotEmpty() bool { - return NotEmpty(b.exec()) -} -func (b *lazyBuilder) Product() float64 { - return Product(b.exec()) -} -func (b *lazyBuilder) Reduce(reduceFunc, acc interface{}) interface{} { - return Reduce(b.exec(), reduceFunc, acc) -} -func (b *lazyBuilder) Sum() float64 { - return Sum(b.exec()) -} -func (b *lazyBuilder) Type() reflect.Type { - return reflect.TypeOf(b.exec()) -} -func (b *lazyBuilder) Value() interface{} { - return b.exec() -} -func (b *lazyBuilder) Values() interface{} { - return Values(b.exec()) -} diff --git a/vendor/github.com/thoas/go-funk/map.go b/vendor/github.com/thoas/go-funk/map.go deleted file mode 100644 index 4dfb4f9047895..0000000000000 --- a/vendor/github.com/thoas/go-funk/map.go +++ /dev/null @@ -1,74 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -// Keys creates an array of the own enumerable map keys or struct field names. -func Keys(out interface{}) interface{} { - value := redirectValue(reflect.ValueOf(out)) - valueType := value.Type() - - if value.Kind() == reflect.Map { - keys := value.MapKeys() - - length := len(keys) - - resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) - - for i, key := range keys { - resultSlice.Index(i).Set(key) - } - - return resultSlice.Interface() - } - - if value.Kind() == reflect.Struct { - length := value.NumField() - - resultSlice := make([]string, length) - - for i := 0; i < length; i++ { - resultSlice[i] = valueType.Field(i).Name - } - - return resultSlice - } - - panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) -} - -// Values creates an array of the own enumerable map values or struct field values. -func Values(out interface{}) interface{} { - value := redirectValue(reflect.ValueOf(out)) - valueType := value.Type() - - if value.Kind() == reflect.Map { - keys := value.MapKeys() - - length := len(keys) - - resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Elem()), length, length) - - for i, key := range keys { - resultSlice.Index(i).Set(value.MapIndex(key)) - } - - return resultSlice.Interface() - } - - if value.Kind() == reflect.Struct { - length := value.NumField() - - resultSlice := make([]interface{}, length) - - for i := 0; i < length; i++ { - resultSlice[i] = value.Field(i).Interface() - } - - return resultSlice - } - - panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) -} diff --git a/vendor/github.com/thoas/go-funk/max.go b/vendor/github.com/thoas/go-funk/max.go deleted file mode 100644 index 4b3914fcd4b68..0000000000000 --- a/vendor/github.com/thoas/go-funk/max.go +++ /dev/null @@ -1,194 +0,0 @@ -package funk - -import "strings" - -// MaxInt validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []int -// It returns int or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxInt(i []int) interface{} { - if len(i) == 0 { - return nil - } - var max int - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxInt8 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []int8 -// It returns int8 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxInt8(i []int8) interface{} { - if len(i) == 0 { - return nil - } - var max int8 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxInt16 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []int16 -// It returns int16 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxInt16(i []int16) interface{} { - if len(i) == 0 { - return nil - } - var max int16 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxInt32 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []int32 -// It returns int32 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxInt32(i []int32) interface{} { - if len(i) == 0 { - return nil - } - var max int32 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxInt64 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []int64 -// It returns int64 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxInt64(i []int64) interface{} { - if len(i) == 0 { - return nil - } - var max int64 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxFloat32 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []float32 -// It returns float32 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxFloat32(i []float32) interface{} { - if len(i) == 0 { - return nil - } - var max float32 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxFloat64 validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []float64 -// It returns float64 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxFloat64(i []float64) interface{} { - if len(i) == 0 { - return nil - } - var max float64 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - if item > max { - max = item - } - } - return max -} - -// MaxString validates the input, compares the elements and returns the maximum element in an array/slice. -// It accepts []string -// It returns string or nil -// It returns nil for the following cases: -// - input is of length 0 -func MaxString(i []string) interface{} { - if len(i) == 0 { - return nil - } - var max string - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - max = item - continue - } - max = compareStringsMax(max, item) - } - return max -} - -// compareStrings uses the strings.Compare method to compare two strings, and returns the greater one. -func compareStringsMax(max, current string) string { - r := strings.Compare(strings.ToLower(max), strings.ToLower(current)) - if r > 0 { - return max - } - return current -} \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/min.go b/vendor/github.com/thoas/go-funk/min.go deleted file mode 100644 index e880f7cf99874..0000000000000 --- a/vendor/github.com/thoas/go-funk/min.go +++ /dev/null @@ -1,193 +0,0 @@ -package funk - -import "strings" - -// MinInt validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []int -// It returns int or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinInt(i []int) interface{} { - if len(i) == 0 { - return nil - } - var min int - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinInt8 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []int8 -// It returns int8 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinInt8(i []int8) interface{} { - if len(i) == 0 { - return nil - } - var min int8 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinInt16 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []int16 -// It returns int16 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinInt16(i []int16) interface{} { - if len(i) == 0 { - return nil - } - var min int16 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinInt32 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []int32 -// It returns int32 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinInt32(i []int32) interface{} { - if len(i) == 0 { - return nil - } - var min int32 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinInt64 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []int64 -// It returns int64 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinInt64(i []int64) interface{} { - if len(i) == 0 { - return nil - } - var min int64 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinFloat32 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []float32 -// It returns float32 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinFloat32(i []float32) interface{} { - if len(i) == 0 { - return nil - } - var min float32 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinFloat64 validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []float64 -// It returns float64 or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinFloat64(i []float64) interface{} { - if len(i) == 0 { - return nil - } - var min float64 - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - if item < min { - min = item - } - } - return min -} - -// MinString validates the input, compares the elements and returns the minimum element in an array/slice. -// It accepts []string -// It returns string or nil -// It returns nil for the following cases: -// - input is of length 0 -func MinString(i []string) interface{} { - if len(i) == 0 { - return nil - } - var min string - for idx := 0; idx < len(i); idx++ { - item := i[idx] - if idx == 0 { - min = item - continue - } - min = compareStringsMin(min, item) - } - return min -} - -func compareStringsMin(min, current string) string { - r := strings.Compare(strings.ToLower(min), strings.ToLower(current)) - if r < 0 { - return min - } - return current -} \ No newline at end of file diff --git a/vendor/github.com/thoas/go-funk/operation.go b/vendor/github.com/thoas/go-funk/operation.go deleted file mode 100644 index 170948b4a6ca9..0000000000000 --- a/vendor/github.com/thoas/go-funk/operation.go +++ /dev/null @@ -1,69 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -func calculate(arr interface{}, name string, operation rune) float64 { - value := redirectValue(reflect.ValueOf(arr)) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - if length == 0 { - return 0 - } - - result := map[rune]float64{ - '+': 0.0, - '*': 1, - }[operation] - - for i := 0; i < length; i++ { - elem := redirectValue(value.Index(i)).Interface() - - var value float64 - switch e := elem.(type) { - case int: - value = float64(e) - case int8: - value = float64(e) - case int16: - value = float64(e) - case int32: - value = float64(e) - case int64: - value = float64(e) - case float32: - value = float64(e) - case float64: - value = e - } - - switch operation { - case '+': - result += value - case '*': - result *= value - } - } - - return result - } - - panic(fmt.Sprintf("Type %s is not supported by %s", valueType.String(), name)) -} - -// Sum computes the sum of the values in array. -func Sum(arr interface{}) float64 { - return calculate(arr, "Sum", '+') -} - -// Product computes the product of the values in array. -func Product(arr interface{}) float64 { - return calculate(arr, "Product", '*') -} diff --git a/vendor/github.com/thoas/go-funk/permutation.go b/vendor/github.com/thoas/go-funk/permutation.go deleted file mode 100644 index 0b109930625cb..0000000000000 --- a/vendor/github.com/thoas/go-funk/permutation.go +++ /dev/null @@ -1,29 +0,0 @@ -package funk - -import "errors" - -// NextPermutation Implement next permutation, -// which rearranges numbers into the lexicographically next greater permutation of numbers. -func NextPermutation(nums []int) error { - n := len(nums) - if n == 0 { - return errors.New("nums is empty") - } - - i := n - 2 - - for i >= 0 && nums[i] >= nums[i+1] { - i-- - } - - if i >= 0 { - j := n - 1 - for j >= 0 && nums[i] >= nums[j] { - j-- - } - nums[i], nums[j] = nums[j], nums[i] - } - - ReverseInt(nums[i+1:]) - return nil -} diff --git a/vendor/github.com/thoas/go-funk/predicate.go b/vendor/github.com/thoas/go-funk/predicate.go deleted file mode 100644 index e6b4131de2e6c..0000000000000 --- a/vendor/github.com/thoas/go-funk/predicate.go +++ /dev/null @@ -1,47 +0,0 @@ -package funk - -import ( - "reflect" -) - -// predicatesImpl contains the common implementation of AnyPredicates and AllPredicates. -func predicatesImpl(value interface{}, wantedAnswer bool, predicates interface{}) bool { - if !IsCollection(predicates) { - panic("Predicates parameter must be an iteratee") - } - - predicatesValue := reflect.ValueOf(predicates) - inputValue := reflect.ValueOf(value) - - for i := 0; i < predicatesValue.Len(); i++ { - funcValue := predicatesValue.Index(i) - if !IsFunction(funcValue.Interface()) { - panic("Got non function as predicate") - } - - funcType := funcValue.Type() - if !IsPredicate(funcValue.Interface()) { - panic("Predicate function must have 1 parameter and must return boolean") - } - - if !inputValue.Type().ConvertibleTo(funcType.In(0)) { - panic("Given value is not compatible with type of parameter for the predicate.") - } - if result := funcValue.Call([]reflect.Value{inputValue}); wantedAnswer == result[0].Bool() { - return wantedAnswer - } - } - - return !wantedAnswer -} - -// AnyPredicates gets a value and a series of predicates, and return true if at least one of the predicates -// is true. -func AnyPredicates(value interface{}, predicates interface{}) bool { - return predicatesImpl(value, true, predicates) -} - -// AllPredicates gets a value and a series of predicates, and return true if all of the predicates are true. -func AllPredicates(value interface{}, predicates interface{}) bool { - return predicatesImpl(value, false, predicates) -} diff --git a/vendor/github.com/thoas/go-funk/presence.go b/vendor/github.com/thoas/go-funk/presence.go deleted file mode 100644 index 9ab0f0f42703a..0000000000000 --- a/vendor/github.com/thoas/go-funk/presence.go +++ /dev/null @@ -1,207 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" - "strings" -) - -// Filter iterates over elements of collection, returning an array of -// all elements predicate returns truthy for. -func Filter(arr interface{}, predicate interface{}) interface{} { - if !IsIteratee(arr) { - panic("First parameter must be an iteratee") - } - - if !IsFunction(predicate, 1, 1) { - panic("Second argument must be function") - } - - funcValue := reflect.ValueOf(predicate) - - funcType := funcValue.Type() - - if funcType.Out(0).Kind() != reflect.Bool { - panic("Return argument should be a boolean") - } - - arrValue := reflect.ValueOf(arr) - - arrType := arrValue.Type() - - // Get slice type corresponding to array type - resultSliceType := reflect.SliceOf(arrType.Elem()) - - // MakeSlice takes a slice kind type, and makes a slice. - resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) - - for i := 0; i < arrValue.Len(); i++ { - elem := arrValue.Index(i) - - result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) - - if result { - resultSlice = reflect.Append(resultSlice, elem) - } - } - - return resultSlice.Interface() -} - -// Find iterates over elements of collection, returning the first -// element predicate returns truthy for. -func Find(arr interface{}, predicate interface{}) interface{} { - _, val := FindKey(arr, predicate) - return val -} - -// Find iterates over elements of collection, returning the first -// element of an array and random of a map which predicate returns truthy for. -func FindKey(arr interface{}, predicate interface{}) (matchKey, matchEle interface{}) { - if !IsIteratee(arr) { - panic("First parameter must be an iteratee") - } - - if !IsFunction(predicate, 1, 1) { - panic("Second argument must be function") - } - - funcValue := reflect.ValueOf(predicate) - - funcType := funcValue.Type() - - if funcType.Out(0).Kind() != reflect.Bool { - panic("Return argument should be a boolean") - } - - arrValue := reflect.ValueOf(arr) - var keyArrs []reflect.Value - - isMap := arrValue.Kind() == reflect.Map - if isMap { - keyArrs = arrValue.MapKeys() - } - for i := 0; i < arrValue.Len(); i++ { - var ( - elem reflect.Value - key reflect.Value - ) - if isMap { - key = keyArrs[i] - elem = arrValue.MapIndex(key) - } else { - key = reflect.ValueOf(i) - elem = arrValue.Index(i) - } - - result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) - - if result { - return key.Interface(), elem.Interface() - } - } - - return nil, nil -} - -// IndexOf gets the index at which the first occurrence of value is found in array or return -1 -// if the value cannot be found -func IndexOf(in interface{}, elem interface{}) int { - inValue := reflect.ValueOf(in) - - elemValue := reflect.ValueOf(elem) - - inType := inValue.Type() - - if inType.Kind() == reflect.String { - return strings.Index(inValue.String(), elemValue.String()) - } - - if inType.Kind() == reflect.Slice { - equalTo := equal(elem) - for i := 0; i < inValue.Len(); i++ { - if equalTo(reflect.Value{}, inValue.Index(i)) { - return i - } - } - } - - return -1 -} - -// LastIndexOf gets the index at which the last occurrence of value is found in array or return -1 -// if the value cannot be found -func LastIndexOf(in interface{}, elem interface{}) int { - inValue := reflect.ValueOf(in) - - elemValue := reflect.ValueOf(elem) - - inType := inValue.Type() - - if inType.Kind() == reflect.String { - return strings.LastIndex(inValue.String(), elemValue.String()) - } - - if inType.Kind() == reflect.Slice { - length := inValue.Len() - - equalTo := equal(elem) - for i := length - 1; i >= 0; i-- { - if equalTo(reflect.Value{}, inValue.Index(i)) { - return i - } - } - } - - return -1 -} - -// Contains returns true if an element is present in a iteratee. -func Contains(in interface{}, elem interface{}) bool { - inValue := reflect.ValueOf(in) - elemValue := reflect.ValueOf(elem) - inType := inValue.Type() - - switch inType.Kind() { - case reflect.String: - return strings.Contains(inValue.String(), elemValue.String()) - case reflect.Map: - equalTo := equal(elem, true) - for _, key := range inValue.MapKeys() { - if equalTo(key, inValue.MapIndex(key)) { - return true - } - } - case reflect.Slice, reflect.Array: - equalTo := equal(elem) - for i := 0; i < inValue.Len(); i++ { - if equalTo(reflect.Value{}, inValue.Index(i)) { - return true - } - } - default: - panic(fmt.Sprintf("Type %s is not supported by Contains, supported types are String, Map, Slice, Array", inType.String())) - } - - return false -} - -// Every returns true if every element is present in a iteratee. -func Every(in interface{}, elements ...interface{}) bool { - for _, elem := range elements { - if !Contains(in, elem) { - return false - } - } - return true -} - -// Some returns true if atleast one element is present in an iteratee. -func Some(in interface{}, elements ...interface{}) bool { - for _, elem := range elements { - if Contains(in, elem) { - return true - } - } - return false -} diff --git a/vendor/github.com/thoas/go-funk/reduce.go b/vendor/github.com/thoas/go-funk/reduce.go deleted file mode 100644 index 579b82254d7ae..0000000000000 --- a/vendor/github.com/thoas/go-funk/reduce.go +++ /dev/null @@ -1,87 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Reduce takes a collection and reduces it to a single value using a reduction -// function (or a valid symbol) and an accumulator value. -func Reduce(arr, reduceFunc, acc interface{}) interface{} { - arrValue := redirectValue(reflect.ValueOf(arr)) - - if !IsIteratee(arrValue.Interface()) { - panic("First parameter must be an iteratee") - } - - returnType := reflect.TypeOf(Reduce).Out(0) - - isFunc := IsFunction(reduceFunc, 2, 1) - isRune := reflect.TypeOf(reduceFunc).Kind() == reflect.Int32 - - if !(isFunc || isRune) { - panic("Second argument must be a valid function or rune") - } - - accValue := reflect.ValueOf(acc) - sliceElemType := sliceElem(arrValue.Type()) - - if isRune { - if arrValue.Kind() == reflect.Slice && sliceElemType.Kind() == reflect.Interface { - accValue = accValue.Convert(returnType) - } else { - accValue = accValue.Convert(sliceElemType) - } - } else { - accValue = accValue.Convert(reflect.TypeOf(reduceFunc).In(0)) - } - - accType := accValue.Type() - - // Generate reduce function if was passed as rune - if isRune { - reduceSign := reduceFunc.(int32) - - if ok := map[rune]bool{'+': true, '*': true}[reduceSign]; !ok { - panic("Invalid reduce sign, allowed: '+' and '*'") - } - - in := []reflect.Type{accType, sliceElemType} - out := []reflect.Type{accType} - funcType := reflect.FuncOf(in, out, false) - - reduceFunc = reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value { - acc := args[0].Interface() - elem := args[1].Interface() - - var result float64 - params := []interface{}{acc, elem} - switch reduceSign { - case '+': - result = Sum(params) - case '*': - result = Product(params) - } - - return []reflect.Value{reflect.ValueOf(result).Convert(accType)} - }).Interface() - } - - funcValue := reflect.ValueOf(reduceFunc) - funcType := funcValue.Type() - - for i := 0; i < arrValue.Len(); i++ { - if accType.ConvertibleTo(funcType.In(0)) { - accValue = accValue.Convert(funcType.In(0)) - } - - arrElementValue := arrValue.Index(i) - if sliceElemType.ConvertibleTo(funcType.In(1)) { - arrElementValue = arrElementValue.Convert(funcType.In(1)) - } - - result := funcValue.Call([]reflect.Value{accValue, arrElementValue}) - accValue = result[0] - } - - return accValue.Convert(returnType).Interface() -} diff --git a/vendor/github.com/thoas/go-funk/retrieve.go b/vendor/github.com/thoas/go-funk/retrieve.go deleted file mode 100644 index 5a358fe0a657f..0000000000000 --- a/vendor/github.com/thoas/go-funk/retrieve.go +++ /dev/null @@ -1,93 +0,0 @@ -package funk - -import ( - "reflect" - "strings" -) - -// Get retrieves the value at path of struct(s). -func Get(out interface{}, path string) interface{} { - result := get(reflect.ValueOf(out), path) - - if result.Kind() != reflect.Invalid && !result.IsZero() { - return result.Interface() - } - - return nil -} - -func get(value reflect.Value, path string) reflect.Value { - if value.Kind() == reflect.Slice || value.Kind() == reflect.Array { - var resultSlice reflect.Value - - length := value.Len() - - if length == 0 { - zeroElement := reflect.Zero(value.Type().Elem()) - pathValue := get(zeroElement, path) - value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0) - - return value - } - - for i := 0; i < length; i++ { - item := value.Index(i) - - resultValue := get(item, path) - - if resultValue.Kind() == reflect.Invalid || resultValue.IsZero() { - continue - } - - resultType := resultValue.Type() - - if resultSlice.Kind() == reflect.Invalid { - resultType := reflect.SliceOf(resultType) - - resultSlice = reflect.MakeSlice(resultType, 0, 0) - } - - resultSlice = reflect.Append(resultSlice, resultValue) - } - - // if the result is a slice of a slice, we need to flatten it - if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice { - return flattenDeep(resultSlice) - } - - return resultSlice - } - - parts := strings.Split(path, ".") - - for _, part := range parts { - value = redirectValue(value) - kind := value.Kind() - - switch kind { - case reflect.Invalid: - continue - case reflect.Struct: - value = value.FieldByName(part) - case reflect.Map: - value = value.MapIndex(reflect.ValueOf(part)) - case reflect.Slice, reflect.Array: - value = get(value, part) - default: - return reflect.ValueOf(nil) - } - } - - return value -} - -// Get retrieves the value of the pointer or default. -func GetOrElse(v interface{}, def interface{}) interface{} { - val := reflect.ValueOf(v) - if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) { - return def - } else if val.Kind() != reflect.Ptr { - return v - } - return val.Elem().Interface() -} diff --git a/vendor/github.com/thoas/go-funk/scan.go b/vendor/github.com/thoas/go-funk/scan.go deleted file mode 100644 index d0d1d454f6518..0000000000000 --- a/vendor/github.com/thoas/go-funk/scan.go +++ /dev/null @@ -1,192 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -// ForEach iterates over elements of collection and invokes iteratee -// for each element. -func ForEach(arr interface{}, predicate interface{}) { - if !IsIteratee(arr) { - panic("First parameter must be an iteratee") - } - - var ( - funcValue = reflect.ValueOf(predicate) - arrValue = reflect.ValueOf(arr) - arrType = arrValue.Type() - funcType = funcValue.Type() - ) - - if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { - if !IsFunction(predicate, 1, 0) { - panic("Second argument must be a function with one parameter") - } - - arrElemType := arrValue.Type().Elem() - - // Checking whether element type is convertible to function's first argument's type. - if !arrElemType.ConvertibleTo(funcType.In(0)) { - panic("Map function's argument is not compatible with type of array.") - } - - for i := 0; i < arrValue.Len(); i++ { - funcValue.Call([]reflect.Value{arrValue.Index(i)}) - } - } - - if arrType.Kind() == reflect.Map { - if !IsFunction(predicate, 2, 0) { - panic("Second argument must be a function with two parameters") - } - - // Type checking for Map = (key, value) - keyType := arrType.Key() - valueType := arrType.Elem() - - if !keyType.ConvertibleTo(funcType.In(0)) { - panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) - } - - if !valueType.ConvertibleTo(funcType.In(1)) { - panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) - } - - for _, key := range arrValue.MapKeys() { - funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) - } - } -} - -// ForEachRight iterates over elements of collection from the right and invokes iteratee -// for each element. -func ForEachRight(arr interface{}, predicate interface{}) { - if !IsIteratee(arr) { - panic("First parameter must be an iteratee") - } - - var ( - funcValue = reflect.ValueOf(predicate) - arrValue = reflect.ValueOf(arr) - arrType = arrValue.Type() - funcType = funcValue.Type() - ) - - if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { - if !IsFunction(predicate, 1, 0) { - panic("Second argument must be a function with one parameter") - } - - arrElemType := arrValue.Type().Elem() - - // Checking whether element type is convertible to function's first argument's type. - if !arrElemType.ConvertibleTo(funcType.In(0)) { - panic("Map function's argument is not compatible with type of array.") - } - - for i := arrValue.Len() - 1; i >= 0; i-- { - funcValue.Call([]reflect.Value{arrValue.Index(i)}) - } - } - - if arrType.Kind() == reflect.Map { - if !IsFunction(predicate, 2, 0) { - panic("Second argument must be a function with two parameters") - } - - // Type checking for Map = (key, value) - keyType := arrType.Key() - valueType := arrType.Elem() - - if !keyType.ConvertibleTo(funcType.In(0)) { - panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) - } - - if !valueType.ConvertibleTo(funcType.In(1)) { - panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) - } - - keys := Reverse(arrValue.MapKeys()).([]reflect.Value) - - for _, key := range keys { - funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) - } - } -} - -// Head gets the first element of array. -func Head(arr interface{}) interface{} { - value := redirectValue(reflect.ValueOf(arr)) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - if value.Len() == 0 { - return nil - } - - return value.Index(0).Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Head", valueType.String())) -} - -// Last gets the last element of array. -func Last(arr interface{}) interface{} { - value := redirectValue(reflect.ValueOf(arr)) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - if value.Len() == 0 { - return nil - } - - return value.Index(value.Len() - 1).Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Last", valueType.String())) -} - -// Initial gets all but the last element of array. -func Initial(arr interface{}) interface{} { - value := redirectValue(reflect.ValueOf(arr)) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - if length <= 1 { - return arr - } - - return value.Slice(0, length-1).Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) -} - -// Tail gets all but the first element of array. -func Tail(arr interface{}) interface{} { - value := redirectValue(reflect.ValueOf(arr)) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - if length <= 1 { - return arr - } - - return value.Slice(1, length).Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) -} diff --git a/vendor/github.com/thoas/go-funk/short_if.go b/vendor/github.com/thoas/go-funk/short_if.go deleted file mode 100644 index 347c03b6205fa..0000000000000 --- a/vendor/github.com/thoas/go-funk/short_if.go +++ /dev/null @@ -1,8 +0,0 @@ -package funk - -func ShortIf(condition bool, a interface{}, b interface{}) interface{} { - if condition { - return a - } - return b -} diff --git a/vendor/github.com/thoas/go-funk/subset.go b/vendor/github.com/thoas/go-funk/subset.go deleted file mode 100644 index de47be1517621..0000000000000 --- a/vendor/github.com/thoas/go-funk/subset.go +++ /dev/null @@ -1,41 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Subset returns true if collection x is a subset of y. -func Subset(x interface{}, y interface{}) bool { - if !IsCollection(x) { - panic("First parameter must be a collection") - } - if !IsCollection(y) { - panic("Second parameter must be a collection") - } - - xValue := reflect.ValueOf(x) - xType := xValue.Type() - - yValue := reflect.ValueOf(y) - yType := yValue.Type() - - if NotEqual(xType, yType) { - panic("Parameters must have the same type") - } - - if xValue.Len() == 0 { - return true - } - - if yValue.Len() == 0 || yValue.Len() < xValue.Len() { - return false - } - - for i := 0; i < xValue.Len(); i++ { - if !Contains(yValue.Interface(), xValue.Index(i).Interface()) { - return false - } - } - - return true -} diff --git a/vendor/github.com/thoas/go-funk/subtraction.go b/vendor/github.com/thoas/go-funk/subtraction.go deleted file mode 100644 index 10a0a982021e8..0000000000000 --- a/vendor/github.com/thoas/go-funk/subtraction.go +++ /dev/null @@ -1,87 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Subtract returns the subtraction between two collections. -func Subtract(x interface{}, y interface{}) interface{} { - if !IsCollection(x) { - panic("First parameter must be a collection") - } - if !IsCollection(y) { - panic("Second parameter must be a collection") - } - - hash := map[interface{}]struct{}{} - - xValue := reflect.ValueOf(x) - xType := xValue.Type() - - yValue := reflect.ValueOf(y) - yType := yValue.Type() - - if NotEqual(xType, yType) { - panic("Parameters must have the same type") - } - - zType := reflect.SliceOf(xType.Elem()) - zSlice := reflect.MakeSlice(zType, 0, 0) - - for i := 0; i < xValue.Len(); i++ { - v := xValue.Index(i).Interface() - hash[v] = struct{}{} - } - - for i := 0; i < yValue.Len(); i++ { - v := yValue.Index(i).Interface() - _, ok := hash[v] - if ok { - delete(hash, v) - } - } - - for i := 0; i < xValue.Len(); i++ { - v := xValue.Index(i).Interface() - _, ok := hash[v] - if ok { - zSlice = reflect.Append(zSlice, xValue.Index(i)) - } - } - - return zSlice.Interface() -} - -// SubtractString returns the subtraction between two collections of string -func SubtractString(x []string, y []string) []string { - if len(x) == 0 { - return []string{} - } - - if len(y) == 0 { - return x - } - - slice := []string{} - hash := map[string]struct{}{} - - for _, v := range x { - hash[v] = struct{}{} - } - - for _, v := range y { - _, ok := hash[v] - if ok { - delete(hash, v) - } - } - - for _, v := range x { - _, ok := hash[v] - if ok { - slice = append(slice, v) - } - } - - return slice -} diff --git a/vendor/github.com/thoas/go-funk/transform.go b/vendor/github.com/thoas/go-funk/transform.go deleted file mode 100644 index e4fd65bb26b98..0000000000000 --- a/vendor/github.com/thoas/go-funk/transform.go +++ /dev/null @@ -1,555 +0,0 @@ -package funk - -import ( - "fmt" - "math/rand" - "reflect" - "strings" -) - -// Chunk creates an array of elements split into groups with the length of size. -// If array can't be split evenly, the final chunk will be -// the remaining element. -func Chunk(arr interface{}, size int) interface{} { - if !IsIteratee(arr) { - panic("First parameter must be neither array nor slice") - } - - if size == 0 { - return arr - } - - arrValue := reflect.ValueOf(arr) - - arrType := arrValue.Type() - - resultSliceType := reflect.SliceOf(arrType) - - // Initialize final result slice which will contains slice - resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) - - itemType := arrType.Elem() - - var itemSlice reflect.Value - - itemSliceType := reflect.SliceOf(itemType) - - length := arrValue.Len() - - for i := 0; i < length; i++ { - if i%size == 0 || i == 0 { - if itemSlice.Kind() != reflect.Invalid { - resultSlice = reflect.Append(resultSlice, itemSlice) - } - - itemSlice = reflect.MakeSlice(itemSliceType, 0, 0) - } - - itemSlice = reflect.Append(itemSlice, arrValue.Index(i)) - - if i == length-1 { - resultSlice = reflect.Append(resultSlice, itemSlice) - } - } - - return resultSlice.Interface() -} - -// ToMap transforms a slice of instances to a Map. -// []*Foo => Map -func ToMap(in interface{}, pivot string) interface{} { - value := reflect.ValueOf(in) - - // input value must be a slice - if value.Kind() != reflect.Slice { - panic(fmt.Sprintf("%v must be a slice", in)) - } - - inType := value.Type() - - structType := inType.Elem() - - // retrieve the struct in the slice to deduce key type - if structType.Kind() == reflect.Ptr { - structType = structType.Elem() - } - - field, _ := structType.FieldByName(pivot) - - // value of the map will be the input type - collectionType := reflect.MapOf(field.Type, inType.Elem()) - - // create a map from scratch - collection := reflect.MakeMap(collectionType) - - for i := 0; i < value.Len(); i++ { - instance := value.Index(i) - var field reflect.Value - - if instance.Kind() == reflect.Ptr { - field = instance.Elem().FieldByName(pivot) - } else { - field = instance.FieldByName(pivot) - } - - collection.SetMapIndex(field, instance) - } - - return collection.Interface() -} - -func mapSlice(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { - funcType := funcValue.Type() - - if funcType.NumIn() != 1 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { - panic("Map function with an array must have one parameter and must return one or two parameters") - } - - arrElemType := arrValue.Type().Elem() - - // Checking whether element type is convertible to function's first argument's type. - if !arrElemType.ConvertibleTo(funcType.In(0)) { - panic("Map function's argument is not compatible with type of array.") - } - - if funcType.NumOut() == 1 { - // Get slice type corresponding to function's return value's type. - resultSliceType := reflect.SliceOf(funcType.Out(0)) - - // MakeSlice takes a slice kind type, and makes a slice. - resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) - - for i := 0; i < arrValue.Len(); i++ { - result := funcValue.Call([]reflect.Value{arrValue.Index(i)})[0] - - resultSlice = reflect.Append(resultSlice, result) - } - - return resultSlice - } - - if funcType.NumOut() == 2 { - // value of the map will be the input type - collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) - - // create a map from scratch - collection := reflect.MakeMap(collectionType) - - for i := 0; i < arrValue.Len(); i++ { - results := funcValue.Call([]reflect.Value{arrValue.Index(i)}) - - collection.SetMapIndex(results[0], results[1]) - } - - return collection - } - - return reflect.Value{} -} - -func mapMap(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { - funcType := funcValue.Type() - - if funcType.NumIn() != 2 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { - panic("Map function with a map must have two parameters and must return one or two parameters") - } - - // Only one returned parameter, should be a slice - if funcType.NumOut() == 1 { - // Get slice type corresponding to function's return value's type. - resultSliceType := reflect.SliceOf(funcType.Out(0)) - - // MakeSlice takes a slice kind type, and makes a slice. - resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) - - for _, key := range arrValue.MapKeys() { - results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) - - result := results[0] - - resultSlice = reflect.Append(resultSlice, result) - } - - return resultSlice - } - - // two parameters, should be a map - if funcType.NumOut() == 2 { - // value of the map will be the input type - collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) - - // create a map from scratch - collection := reflect.MakeMap(collectionType) - - for _, key := range arrValue.MapKeys() { - results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) - - collection.SetMapIndex(results[0], results[1]) - - } - - return collection - } - - return reflect.Value{} -} - -// Map manipulates an iteratee and transforms it to another type. -func Map(arr interface{}, mapFunc interface{}) interface{} { - result := mapFn(arr, mapFunc, "Map") - - if result.IsValid() { - return result.Interface() - } - - return nil -} - -func mapFn(arr interface{}, mapFunc interface{}, funcName string) reflect.Value { - if !IsIteratee(arr) { - panic("First parameter must be an iteratee") - } - - if !IsFunction(mapFunc) { - panic("Second argument must be function") - } - - var ( - funcValue = reflect.ValueOf(mapFunc) - arrValue = reflect.ValueOf(arr) - arrType = arrValue.Type() - ) - - kind := arrType.Kind() - - if kind == reflect.Slice || kind == reflect.Array { - return mapSlice(arrValue, funcValue) - } else if kind == reflect.Map { - return mapMap(arrValue, funcValue) - } - - panic(fmt.Sprintf("Type %s is not supported by "+funcName, arrType.String())) -} - -// FlatMap manipulates an iteratee and transforms it to a flattened collection of another type. -func FlatMap(arr interface{}, mapFunc interface{}) interface{} { - result := mapFn(arr, mapFunc, "FlatMap") - - if result.IsValid() { - return flatten(result).Interface() - } - - return nil -} - -// Flatten flattens a two-dimensional array. -func Flatten(out interface{}) interface{} { - return flatten(reflect.ValueOf(out)).Interface() -} - -func flatten(value reflect.Value) reflect.Value { - sliceType := value.Type() - - if (value.Kind() != reflect.Slice && value.Kind() != reflect.Array) || - (sliceType.Elem().Kind() != reflect.Slice && sliceType.Elem().Kind() != reflect.Array) { - panic("Argument must be an array or slice of at least two dimensions") - } - - resultSliceType := sliceType.Elem().Elem() - - resultSlice := reflect.MakeSlice(reflect.SliceOf(resultSliceType), 0, 0) - - length := value.Len() - - for i := 0; i < length; i++ { - item := value.Index(i) - - resultSlice = reflect.AppendSlice(resultSlice, item) - } - - return resultSlice -} - -// FlattenDeep recursively flattens array. -func FlattenDeep(out interface{}) interface{} { - return flattenDeep(reflect.ValueOf(out)).Interface() -} - -func flattenDeep(value reflect.Value) reflect.Value { - sliceType := sliceElem(value.Type()) - - resultSlice := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, 0) - - return flattenRecursive(value, resultSlice) -} - -func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { - length := value.Len() - - for i := 0; i < length; i++ { - item := value.Index(i) - kind := item.Kind() - - if kind == reflect.Slice || kind == reflect.Array { - result = flattenRecursive(item, result) - } else { - result = reflect.Append(result, item) - } - } - - return result -} - -// Shuffle creates an array of shuffled values -func Shuffle(in interface{}) interface{} { - value := reflect.ValueOf(in) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - resultSlice := makeSlice(value, length) - - for i, v := range rand.Perm(length) { - resultSlice.Index(i).Set(value.Index(v)) - } - - return resultSlice.Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Shuffle", valueType.String())) -} - -// Reverse transforms an array the first element will become the last, -// the second element will become the second to last, etc. -func Reverse(in interface{}) interface{} { - value := reflect.ValueOf(in) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.String { - return ReverseString(in.(string)) - } - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - resultSlice := makeSlice(value, length) - - j := 0 - for i := length - 1; i >= 0; i-- { - resultSlice.Index(j).Set(value.Index(i)) - j++ - } - - return resultSlice.Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Reverse", valueType.String())) -} - -// Uniq creates an array with unique values. -func Uniq(in interface{}) interface{} { - value := reflect.ValueOf(in) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - result := makeSlice(value, 0) - - seen := make(map[interface{}]bool, length) - j := 0 - - for i := 0; i < length; i++ { - val := value.Index(i) - v := val.Interface() - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = true - result = reflect.Append(result, val) - j++ - } - - return result.Interface() - } - - panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) -} - -// ConvertSlice converts a slice type to another, -// a perfect example would be to convert a slice of struct to a slice of interface. -func ConvertSlice(in interface{}, out interface{}) { - srcValue := reflect.ValueOf(in) - - dstValue := reflect.ValueOf(out) - - if dstValue.Kind() != reflect.Ptr { - panic("Second argument must be a pointer") - } - - dstValue = dstValue.Elem() - - if srcValue.Kind() != reflect.Slice && srcValue.Kind() != reflect.Array { - panic("First argument must be an array or slice") - } - - if dstValue.Kind() != reflect.Slice && dstValue.Kind() != reflect.Array { - panic("Second argument must be an array or slice") - } - - // returns value that points to dstValue - direct := reflect.Indirect(dstValue) - - length := srcValue.Len() - - for i := 0; i < length; i++ { - dstValue = reflect.Append(dstValue, srcValue.Index(i)) - } - - direct.Set(dstValue) -} - -// Drop creates an array/slice with `n` elements dropped from the beginning. -func Drop(in interface{}, n int) interface{} { - value := reflect.ValueOf(in) - valueType := value.Type() - - kind := value.Kind() - - if kind == reflect.Array || kind == reflect.Slice { - length := value.Len() - - resultSlice := makeSlice(value, length-n) - - j := 0 - for i := n; i < length; i++ { - resultSlice.Index(j).Set(value.Index(i)) - j++ - } - - return resultSlice.Interface() - - } - - panic(fmt.Sprintf("Type %s is not supported by Drop", valueType.String())) -} - -// Prune returns a copy of "in" that only contains fields in "paths" -// which are looked up using struct field name. -// For lookup paths by field tag instead, use funk.PruneByTag() -func Prune(in interface{}, paths []string) (interface{}, error) { - return pruneByTag(in, paths, nil /*tag*/) -} - -// pruneByTag returns a copy of "in" that only contains fields in "paths" -// which are looked up using struct field Tag "tag". -func PruneByTag(in interface{}, paths []string, tag string) (interface{}, error) { - return pruneByTag(in, paths, &tag) -} - -// pruneByTag returns a copy of "in" that only contains fields in "paths" -// which are looked up using struct field Tag "tag". If tag is nil, -// traverse paths using struct field name -func pruneByTag(in interface{}, paths []string, tag *string) (interface{}, error) { - - inValue := reflect.ValueOf(in) - - ret := reflect.New(inValue.Type()).Elem() - - for _, path := range paths { - parts := strings.Split(path, ".") - if err := prune(inValue, ret, parts, tag); err != nil { - return nil, err - } - } - return ret.Interface(), nil -} - -func prune(inValue reflect.Value, ret reflect.Value, parts []string, tag *string) error { - - if len(parts) == 0 { - // we reached the location that ret needs to hold inValue - // Note: The value at the end of the path is not copied, maybe we need to change. - // ret and the original data holds the same reference to this value - ret.Set(inValue) - return nil - } - - inKind := inValue.Kind() - - switch inKind { - case reflect.Ptr: - if inValue.IsNil() { - // TODO validate - return nil - } - if ret.IsNil() { - // init ret and go to next level - ret.Set(reflect.New(inValue.Type().Elem())) - } - return prune(inValue.Elem(), ret.Elem(), parts, tag) - case reflect.Struct: - part := parts[0] - var fValue reflect.Value - var fRet reflect.Value - if tag == nil { - // use field name - fValue = inValue.FieldByName(part) - if !fValue.IsValid() { - return fmt.Errorf("field name %v is not found in struct %v", part, inValue.Type().String()) - } - fRet = ret.FieldByName(part) - } else { - // search tag that has key equal to part - found := false - for i := 0; i < inValue.NumField(); i++ { - f := inValue.Type().Field(i) - if key, ok := f.Tag.Lookup(*tag); ok { - if key == part { - fValue = inValue.Field(i) - fRet = ret.Field(i) - found = true - break - } - } - } - if !found { - return fmt.Errorf("Struct tag %v is not found with key %v", *tag, part) - } - } - // init Ret is zero and go down one more level - if fRet.IsZero() { - fRet.Set(reflect.New(fValue.Type()).Elem()) - } - return prune(fValue, fRet, parts[1:], tag) - case reflect.Array, reflect.Slice: - // set all its elements - length := inValue.Len() - // init ret - if ret.IsZero() { - if inKind == reflect.Slice { - ret.Set(reflect.MakeSlice(inValue.Type(), length /*len*/, length /*cap*/)) - } else { // array - ret.Set(reflect.New(inValue.Type()).Elem()) - } - } - for j := 0; j < length; j++ { - if err := prune(inValue.Index(j), ret.Index(j), parts, tag); err != nil { - return err - } - } - default: - return fmt.Errorf("path %v cannot be looked up on kind of %v", strings.Join(parts, "."), inValue.Kind()) - } - - return nil -} diff --git a/vendor/github.com/thoas/go-funk/typesafe.go b/vendor/github.com/thoas/go-funk/typesafe.go deleted file mode 100644 index 3242dc02dc0c9..0000000000000 --- a/vendor/github.com/thoas/go-funk/typesafe.go +++ /dev/null @@ -1,967 +0,0 @@ -package funk - -import ( - "math/rand" -) - -// InInts is an alias of ContainsInt, returns true if an int is present in a iteratee. -func InInts(s []int, v int) bool { - return ContainsInt(s, v) -} - -// InInt32s is an alias of ContainsInt32, returns true if an int32 is present in a iteratee. -func InInt32s(s []int32, v int32) bool { - return ContainsInt32(s, v) -} - -// InInt64s is an alias of ContainsInt64, returns true if an int64 is present in a iteratee. -func InInt64s(s []int64, v int64) bool { - return ContainsInt64(s, v) -} - -// InUInts is an alias of ContainsUInt, returns true if an uint is present in a iteratee. -func InUInts(s []uint, v uint) bool { - return ContainsUInt(s, v) -} - -// InUInt32s is an alias of ContainsUInt32, returns true if an uint32 is present in a iteratee. -func InUInt32s(s []uint32, v uint32) bool { - return ContainsUInt32(s, v) -} - -// InUInt64s is an alias of ContainsUInt64, returns true if an uint64 is present in a iteratee. -func InUInt64s(s []uint64, v uint64) bool { - return ContainsUInt64(s, v) -} - -// InStrings is an alias of ContainsString, returns true if a string is present in a iteratee. -func InStrings(s []string, v string) bool { - return ContainsString(s, v) -} - -// InFloat32s is an alias of ContainsFloat32, returns true if a float32 is present in a iteratee. -func InFloat32s(s []float32, v float32) bool { - return ContainsFloat32(s, v) -} - -// InFloat64s is an alias of ContainsFloat64, returns true if a float64 is present in a iteratee. -func InFloat64s(s []float64, v float64) bool { - return ContainsFloat64(s, v) -} - -// FindFloat64 iterates over a collection of float64, returning an array of -// all float64 elements predicate returns truthy for. -func FindFloat64(s []float64, cb func(s float64) bool) (float64, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return 0.0, false -} - -// FindFloat32 iterates over a collection of float32, returning the first -// float32 element predicate returns truthy for. -func FindFloat32(s []float32, cb func(s float32) bool) (float32, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return 0.0, false -} - -// FindInt iterates over a collection of int, returning the first -// int element predicate returns truthy for. -func FindInt(s []int, cb func(s int) bool) (int, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return 0, false -} - -// FindInt32 iterates over a collection of int32, returning the first -// int32 element predicate returns truthy for. -func FindInt32(s []int32, cb func(s int32) bool) (int32, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return 0, false -} - -// FindInt64 iterates over a collection of int64, returning the first -// int64 element predicate returns truthy for. -func FindInt64(s []int64, cb func(s int64) bool) (int64, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return 0, false -} - -// FindString iterates over a collection of string, returning the first -// string element predicate returns truthy for. -func FindString(s []string, cb func(s string) bool) (string, bool) { - for _, i := range s { - result := cb(i) - - if result { - return i, true - } - } - - return "", false -} - -// FilterFloat64 iterates over a collection of float64, returning an array of -// all float64 elements predicate returns truthy for. -func FilterFloat64(s []float64, cb func(s float64) bool) []float64 { - results := []float64{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterFloat32 iterates over a collection of float32, returning an array of -// all float32 elements predicate returns truthy for. -func FilterFloat32(s []float32, cb func(s float32) bool) []float32 { - results := []float32{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterInt iterates over a collection of int, returning an array of -// all int elements predicate returns truthy for. -func FilterInt(s []int, cb func(s int) bool) []int { - results := []int{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterInt32 iterates over a collection of int32, returning an array of -// all int32 elements predicate returns truthy for. -func FilterInt32(s []int32, cb func(s int32) bool) []int32 { - results := []int32{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterInt64 iterates over a collection of int64, returning an array of -// all int64 elements predicate returns truthy for. -func FilterInt64(s []int64, cb func(s int64) bool) []int64 { - results := []int64{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterUInt iterates over a collection of uint, returning an array of -// all uint elements predicate returns truthy for. -func FilterUInt(s []uint, cb func(s uint) bool) []uint { - results := []uint{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterUInt32 iterates over a collection of uint32, returning an array of -// all uint32 elements predicate returns truthy for. -func FilterUInt32(s []uint32, cb func(s uint32) bool) []uint32 { - results := []uint32{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterUInt64 iterates over a collection of uint64, returning an array of -// all uint64 elements predicate returns truthy for. -func FilterUInt64(s []uint64, cb func(s uint64) bool) []uint64 { - results := []uint64{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// FilterString iterates over a collection of string, returning an array of -// all string elements predicate returns truthy for. -func FilterString(s []string, cb func(s string) bool) []string { - results := []string{} - - for _, i := range s { - result := cb(i) - - if result { - results = append(results, i) - } - } - - return results -} - -// ContainsInt returns true if an int is present in a iteratee. -func ContainsInt(s []int, v int) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsInt32 returns true if an int32 is present in a iteratee. -func ContainsInt32(s []int32, v int32) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsInt64 returns true if an int64 is present in a iteratee. -func ContainsInt64(s []int64, v int64) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsUInt returns true if an uint is present in a iteratee. -func ContainsUInt(s []uint, v uint) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsUInt32 returns true if an uint32 is present in a iteratee. -func ContainsUInt32(s []uint32, v uint32) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsUInt64 returns true if an uint64 is present in a iteratee. -func ContainsUInt64(s []uint64, v uint64) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - - -// ContainsString returns true if a string is present in a iteratee. -func ContainsString(s []string, v string) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsFloat32 returns true if a float32 is present in a iteratee. -func ContainsFloat32(s []float32, v float32) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// ContainsFloat64 returns true if a float64 is present in a iteratee. -func ContainsFloat64(s []float64, v float64) bool { - for _, vv := range s { - if vv == v { - return true - } - } - return false -} - -// SumInt32 sums a int32 iteratee and returns the sum of all elements -func SumInt32(s []int32) (sum int32) { - for _, v := range s { - sum += v - } - return -} - -// SumInt64 sums a int64 iteratee and returns the sum of all elements -func SumInt64(s []int64) (sum int64) { - for _, v := range s { - sum += v - } - return -} - -// SumInt sums a int iteratee and returns the sum of all elements -func SumInt(s []int) (sum int) { - for _, v := range s { - sum += v - } - return -} - -// SumUInt32 sums a uint32 iteratee and returns the sum of all elements -func SumUInt32(s []uint32) (sum uint32) { - for _, v := range s { - sum += v - } - return -} - -// SumUInt64 sums a uint64 iteratee and returns the sum of all elements -func SumUInt64(s []uint64) (sum uint64) { - for _, v := range s { - sum += v - } - return -} - -// SumUInt sums a uint iteratee and returns the sum of all elements -func SumUInt(s []uint) (sum uint) { - for _, v := range s { - sum += v - } - return -} - -// SumFloat64 sums a float64 iteratee and returns the sum of all elements -func SumFloat64(s []float64) (sum float64) { - for _, v := range s { - sum += v - } - return -} - -// SumFloat32 sums a float32 iteratee and returns the sum of all elements -func SumFloat32(s []float32) (sum float32) { - for _, v := range s { - sum += v - } - return -} - -// ReverseStrings reverses an array of string -func ReverseStrings(s []string) []string { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseInt reverses an array of int -func ReverseInt(s []int) []int { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseInt32 reverses an array of int32 -func ReverseInt32(s []int32) []int32 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseInt64 reverses an array of int64 -func ReverseInt64(s []int64) []int64 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseUInt reverses an array of int -func ReverseUInt(s []uint) []uint { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseUInt32 reverses an array of uint32 -func ReverseUInt32(s []uint32) []uint32 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseUInt64 reverses an array of uint64 -func ReverseUInt64(s []uint64) []uint64 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseFloat64 reverses an array of float64 -func ReverseFloat64(s []float64) []float64 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseFloat32 reverses an array of float32 -func ReverseFloat32(s []float32) []float32 { - for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -// ReverseString reverses a string -func ReverseString(s string) string { - r := []rune(s) - for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { - r[i], r[j] = r[j], r[i] - } - return string(r) -} - -func indexOf(n int, f func(int) bool) int { - for i := 0; i < n; i++ { - if f(i) { - return i - } - } - return -1 -} - -// IndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 -// if the value cannot be found -func IndexOfInt(a []int, x int) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 -// if the value cannot be found -func IndexOfInt32(a []int32, x int32) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 -// if the value cannot be found -func IndexOfInt64(a []int64, x int64) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 -// if the value cannot be found -func IndexOfUInt(a []uint, x uint) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 -// if the value cannot be found -func IndexOfUInt32(a []uint32, x uint32) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 -// if the value cannot be found -func IndexOfUInt64(a []uint64, x uint64) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 -// if the value cannot be found -func IndexOfFloat64(a []float64, x float64) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// IndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 -// if the value cannot be found -func IndexOfString(a []string, x string) int { - return indexOf(len(a), func(i int) bool { return a[i] == x }) -} - -func lastIndexOf(n int, f func(int) bool) int { - for i := n - 1; i >= 0; i-- { - if f(i) { - return i - } - } - return -1 -} - -// LastIndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 -// if the value cannot be found -func LastIndexOfInt(a []int, x int) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfInt32(a []int32, x int32) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfInt64(a []int64, x int64) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 -// if the value cannot be found -func LastIndexOfUInt(a []uint, x uint) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfUInt32(a []uint32, x uint32) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfUInt64(a []uint64, x uint64) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfFloat64(a []float64, x float64) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfFloat32 gets the index at which the first occurrence of an float32 value is found in array or return -1 -// if the value cannot be found -func LastIndexOfFloat32(a []float32, x float32) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// LastIndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 -// if the value cannot be found -func LastIndexOfString(a []string, x string) int { - return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) -} - -// UniqInt32 creates an array of int32 with unique values. -func UniqInt32(a []int32) []int32 { - length := len(a) - - seen := make(map[int32]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqInt64 creates an array of int64 with unique values. -func UniqInt64(a []int64) []int64 { - length := len(a) - - seen := make(map[int64]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqInt creates an array of int with unique values. -func UniqInt(a []int) []int { - length := len(a) - - seen := make(map[int]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqUInt32 creates an array of uint32 with unique values. -func UniqUInt32(a []uint32) []uint32 { - length := len(a) - - seen := make(map[uint32]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqUInt64 creates an array of uint64 with unique values. -func UniqUInt64(a []uint64) []uint64 { - length := len(a) - - seen := make(map[uint64]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqUInt creates an array of uint with unique values. -func UniqUInt(a []uint) []uint { - length := len(a) - - seen := make(map[uint]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqString creates an array of string with unique values. -func UniqString(a []string) []string { - length := len(a) - - seen := make(map[string]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqFloat64 creates an array of float64 with unique values. -func UniqFloat64(a []float64) []float64 { - length := len(a) - - seen := make(map[float64]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// UniqFloat32 creates an array of float32 with unique values. -func UniqFloat32(a []float32) []float32 { - length := len(a) - - seen := make(map[float32]struct{}, length) - j := 0 - - for i := 0; i < length; i++ { - v := a[i] - - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - a[j] = v - j++ - } - - return a[0:j] -} - -// ShuffleInt creates an array of int shuffled values using Fisher–Yates algorithm -func ShuffleInt(a []int) []int { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleInt32 creates an array of int32 shuffled values using Fisher–Yates algorithm -func ShuffleInt32(a []int32) []int32 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleInt64 creates an array of int64 shuffled values using Fisher–Yates algorithm -func ShuffleInt64(a []int64) []int64 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleUInt creates an array of int shuffled values using Fisher–Yates algorithm -func ShuffleUInt(a []uint) []uint { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleUInt32 creates an array of uint32 shuffled values using Fisher–Yates algorithm -func ShuffleUInt32(a []uint32) []uint32 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleUInt64 creates an array of uint64 shuffled values using Fisher–Yates algorithm -func ShuffleUInt64(a []uint64) []uint64 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleString creates an array of string shuffled values using Fisher–Yates algorithm -func ShuffleString(a []string) []string { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleFloat32 creates an array of float32 shuffled values using Fisher–Yates algorithm -func ShuffleFloat32(a []float32) []float32 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// ShuffleFloat64 creates an array of float64 shuffled values using Fisher–Yates algorithm -func ShuffleFloat64(a []float64) []float64 { - for i := range a { - j := rand.Intn(i + 1) - a[i], a[j] = a[j], a[i] - } - - return a -} - -// DropString creates a slice with `n` strings dropped from the beginning. -func DropString(s []string, n int) []string { - return s[n:] -} - -// DropInt creates a slice with `n` ints dropped from the beginning. -func DropInt(s []int, n int) []int { - return s[n:] -} - -// DropInt32 creates a slice with `n` int32s dropped from the beginning. -func DropInt32(s []int32, n int) []int32 { - return s[n:] -} - -// DropInt64 creates a slice with `n` int64s dropped from the beginning. -func DropInt64(s []int64, n int) []int64 { - return s[n:] -} - -// DropUInt creates a slice with `n` ints dropped from the beginning. -func DropUInt(s []uint, n uint) []uint { - return s[n:] -} - -// DropUInt32 creates a slice with `n` int32s dropped from the beginning. -func DropUInt32(s []uint32, n int) []uint32 { - return s[n:] -} - -// DropUInt64 creates a slice with `n` int64s dropped from the beginning. -func DropUInt64(s []uint64, n int) []uint64 { - return s[n:] -} - -// DropFloat32 creates a slice with `n` float32s dropped from the beginning. -func DropFloat32(s []float32, n int) []float32 { - return s[n:] -} - -// DropFloat64 creates a slice with `n` float64s dropped from the beginning. -func DropFloat64(s []float64, n int) []float64 { - return s[n:] -} diff --git a/vendor/github.com/thoas/go-funk/utils.go b/vendor/github.com/thoas/go-funk/utils.go deleted file mode 100644 index 43d9a2be73a9d..0000000000000 --- a/vendor/github.com/thoas/go-funk/utils.go +++ /dev/null @@ -1,103 +0,0 @@ -package funk - -import ( - "fmt" - "reflect" -) - -func equal(expectedOrPredicate interface{}, optionalIsMap ...bool) func(keyValueIfMap, actualValue reflect.Value) bool { - isMap := append(optionalIsMap, false)[0] - - if IsFunction(expectedOrPredicate) { - inTypes := []reflect.Type{nil}; if isMap { - inTypes = append(inTypes, nil) - } - - if !IsPredicate(expectedOrPredicate, inTypes...) { - panic(fmt.Sprintf("Predicate function must have %d parameter and must return boolean", len(inTypes))) - } - - predicateValue := reflect.ValueOf(expectedOrPredicate) - - return func(keyValueIfMap, actualValue reflect.Value) bool { - - if isMap && !keyValueIfMap.Type().ConvertibleTo(predicateValue.Type().In(0)) { - panic("Given key is not compatible with type of parameter for the predicate.") - } - - if (isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(1))) || - (!isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(0))) { - panic("Given value is not compatible with type of parameter for the predicate.") - } - - args := []reflect.Value{actualValue} - if isMap { - args = append([]reflect.Value{keyValueIfMap}, args...) - } - - return predicateValue.Call(args)[0].Bool() - } - } - - expected := expectedOrPredicate - - return func(keyValueIfMap, actualValue reflect.Value) bool { - if isMap { - actualValue = keyValueIfMap - } - - if expected == nil || actualValue.IsZero() { - return actualValue.Interface() == expected - } - - return reflect.DeepEqual(actualValue.Interface(), expected) - } -} - -func sliceElem(rtype reflect.Type) reflect.Type { - for { - if rtype.Kind() != reflect.Slice && rtype.Kind() != reflect.Array { - return rtype - } - - rtype = rtype.Elem() - } -} - -func redirectValue(value reflect.Value) reflect.Value { - for { - if !value.IsValid() || (value.Kind() != reflect.Ptr && value.Kind() != reflect.Interface) { - return value - } - - res := value.Elem() - - // Test for a circular type. - if res.Kind() == reflect.Ptr && value.Kind() == reflect.Ptr && value.Pointer() == res.Pointer() { - return value - } - - if !res.IsValid() && value.Kind() == reflect.Ptr { - return reflect.Zero(value.Type().Elem()) - } - - value = res - } -} - -func makeSlice(value reflect.Value, values ...int) reflect.Value { - sliceType := sliceElem(value.Type()) - - size := value.Len() - cap := size - - if len(values) > 0 { - size = values[0] - } - - if len(values) > 1 { - cap = values[1] - } - - return reflect.MakeSlice(reflect.SliceOf(sliceType), size, cap) -} diff --git a/vendor/github.com/thoas/go-funk/without.go b/vendor/github.com/thoas/go-funk/without.go deleted file mode 100644 index 6e35e98699332..0000000000000 --- a/vendor/github.com/thoas/go-funk/without.go +++ /dev/null @@ -1,19 +0,0 @@ -package funk - -import "reflect" - -// Without creates an array excluding all given values. -func Without(in interface{}, values ...interface{}) interface{} { - if !IsCollection(in) { - panic("First parameter must be a collection") - } - - inValue := reflect.ValueOf(in) - for _, value := range values { - if NotEqual(inValue.Type().Elem(), reflect.TypeOf(value)) { - panic("Values must have the same type") - } - } - - return LeftJoin(inValue, reflect.ValueOf(values)).Interface() -} diff --git a/vendor/github.com/thoas/go-funk/zip.go b/vendor/github.com/thoas/go-funk/zip.go deleted file mode 100644 index f9056bc419bcd..0000000000000 --- a/vendor/github.com/thoas/go-funk/zip.go +++ /dev/null @@ -1,46 +0,0 @@ -package funk - -import ( - "reflect" -) - -// Tuple is the return type of Zip -type Tuple struct { - Element1 interface{} - Element2 interface{} -} - -// Zip returns a list of tuples, where the i-th tuple contains the i-th element -// from each of the input iterables. The returned list is truncated in length -// to the length of the shortest input iterable. -func Zip(slice1 interface{}, slice2 interface{}) []Tuple { - inValue1 := reflect.ValueOf(slice1) - inValue2 := reflect.ValueOf(slice2) - kind1 := inValue1.Type().Kind() - kind2 := inValue2.Type().Kind() - - result := []Tuple{} - for _, kind := range []reflect.Kind{kind1, kind2} { - if kind != reflect.Slice && kind != reflect.Array { - return result - } - } - - var minLength int - length1 := inValue1.Len() - length2 := inValue2.Len() - if length1 <= length2 { - minLength = length1 - } else { - minLength = length2 - } - - for i := 0; i < minLength; i++ { - newTuple := Tuple{ - Element1: inValue1.Index(i).Interface(), - Element2: inValue2.Index(i).Interface(), - } - result = append(result, newTuple) - } - return result -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 01007e990cb11..41b6e2199fef0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -761,9 +761,6 @@ github.com/syndtr/goleveldb/leveldb/opt github.com/syndtr/goleveldb/leveldb/storage github.com/syndtr/goleveldb/leveldb/table github.com/syndtr/goleveldb/leveldb/util -# github.com/thoas/go-funk v0.8.0 -## explicit -github.com/thoas/go-funk # github.com/toqueteos/webbrowser v1.2.0 github.com/toqueteos/webbrowser # github.com/tstranex/u2f v1.0.0 From 8339cf95eb0636d8fccd40b23bcd8bd57b9d723b Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 31 Aug 2021 11:24:15 +0200 Subject: [PATCH 04/19] Clean up test database - revert initial Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- integrations/auth_ldap_test.go | 69 ++++++++++++---------------------- 1 file changed, 24 insertions(+), 45 deletions(-) diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index c89f329ae0621..3afb6bdbdd5aa 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -98,7 +98,15 @@ func getLDAPServerHost() string { return host } -func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) { +func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string, groupMapParams ...string) { + teamGroupMapEnabled := "off" + teamGroupMapRemoval := "off" + teamGroupMap := "" + if len(groupMapParams) == 3 { + teamGroupMapEnabled = groupMapParams[0] + teamGroupMapRemoval = groupMapParams[1] + teamGroupMap = groupMapParams[2] + } session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{ @@ -120,12 +128,12 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string) { "attribute_ssh_public_key": sshKeyAttribute, "is_sync_enabled": "on", "is_active": "on", - "team_group_map_enabled": "on", - "team_group_map_removal": "on", + "team_group_map_enabled": teamGroupMapEnabled, + "team_group_map_removal": teamGroupMapRemoval, "group_dn": "ou=people,dc=planetexpress,dc=com", "group_member_uid": "member", "user_uid": "DN", - "team_group_map": "{\"cn=ship_crew,ou=people,dc=planetexpress,dc=com\": {\"org26\": [\"team11\"]},\"cn=admin_staff,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]},\"cn=non-existent,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]}}", + "team_group_map": teamGroupMap, }) session.MakeRequest(t, req, http.StatusFound) } @@ -308,7 +316,7 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { return } defer prepareTestEnv(t)() - addAuthSourceLDAP(t, "") + addAuthSourceLDAP(t, "", "on", "on", "{\"cn=ship_crew,ou=people,dc=planetexpress,dc=com\":{\"org26\": [\"team11\"]},\"cn=admin_staff,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]}}") org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") @@ -324,13 +332,15 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { assert.NoError(t, err) if user.Name == "fry" || user.Name == "leela" || user.Name == "bender" { // assert members of LDAP group "cn=ship_crew" are added to mapped teams - assert.Equal(t, len(usersOrgs), 1, "User should be member of one organization") + assert.Equal(t, len(usersOrgs), 1, "User [%s] should be member of one organization", user.Name) assert.Equal(t, usersOrgs[0].Name, "org26", "Membership should be added to the right organization") isMember, err := models.IsTeamMember(usersOrgs[0].ID, team.ID, user.ID) assert.NoError(t, err) assert.True(t, isMember, "Membership should be added to the right team") err = team.RemoveMember(user.ID) assert.NoError(t, err) + err = usersOrgs[0].RemoveMember(user.ID) + assert.NoError(t, err) } else { // assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist assert.Empty(t, usersOrgs, "User should be member of no organization") @@ -348,14 +358,15 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { return } defer prepareTestEnv(t)() - addAuthSourceLDAP(t, "") - auth.SyncExternalUsers(context.Background(), true) + addAuthSourceLDAP(t, "", "on", "on", "{\"cn=dispatch,ou=people,dc=planetexpress,dc=com\": {\"org26\": [\"team11\"]}}") org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") assert.NoError(t, err) - user, err := models.GetUserByName("professor") - assert.NoError(t, err) + loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password) + user := models.AssertExistsAndLoadBean(t, &models.User{ + Name: gitLDAPUsers[0].UserName, + }).(*models.User) err = org.AddMember(user.ID) assert.NoError(t, err) err = team.AddMember(user.ID) @@ -366,8 +377,8 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID) assert.NoError(t, err) assert.True(t, isMember, "User should be member of this team") - // assert team member "professor" gets removed from "team11" - auth.SyncExternalUsers(context.Background(), true) + // assert team member "professor" gets removed from org26 team11 + loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password) isMember, err = models.IsOrganizationMember(org.ID, user.ID) assert.NoError(t, err) assert.False(t, isMember, "User membership should have been removed from organization") @@ -376,38 +387,6 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { assert.False(t, isMember, "User membership should have been removed from team") } -func addBrokenLDAPMapAuthSource(t *testing.T, sshKeyAttribute string) { - session := loginUser(t, "user1") - csrf := GetCSRF(t, session, "/admin/auths/new") - req := NewRequestWithValues(t, "POST", "/admin/auths/new", map[string]string{ - "_csrf": csrf, - "type": "2", - "name": "ldap", - "host": getLDAPServerHost(), - "port": "389", - "bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com", - "bind_password": "password", - "user_base": "ou=people,dc=planetexpress,dc=com", - "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))", - "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)", - "restricted_filter": "(uid=leela)", - "attribute_username": "uid", - "attribute_name": "givenName", - "attribute_surname": "sn", - "attribute_mail": "mail", - "attribute_ssh_public_key": sshKeyAttribute, - "is_sync_enabled": "on", - "is_active": "on", - "team_group_map_enabled": "on", - "team_group_map_removal": "on", - "group_dn": "ou=people,dc=planetexpress,dc=com", - "group_member_uid": "member", - "user_uid": "DN", - "team_group_map": "{\"NOT_A_VALID_JSON\"[\"MISSING_DOUBLE_POINT\"]}", - }) - session.MakeRequest(t, req, http.StatusFound) -} - // Login should work even if Team Group Map contains a broken JSON func TestBrokenLDAPMapUserSignin(t *testing.T) { if skipLDAPTests() { @@ -415,7 +394,7 @@ func TestBrokenLDAPMapUserSignin(t *testing.T) { return } defer prepareTestEnv(t)() - addBrokenLDAPMapAuthSource(t, "") + addAuthSourceLDAP(t, "", "on", "on", "{\"NOT_A_VALID_JSON\"[\"MISSING_DOUBLE_POINT\"]}") u := gitLDAPUsers[0] From 76bb58892e0cdd80226ac59350a7f289e9b86925 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Thu, 2 Sep 2021 10:00:37 +0200 Subject: [PATCH 05/19] Skip adding team/org members when already member Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- services/auth/source/ldap/source_group_sync.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index 29fdf0375878f..c62ad346ec200 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -24,10 +24,11 @@ func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[s } if isMember, err := models.IsOrganizationMember(org.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to organization [%s]", user.Name, org.Name) - } - err = org.AddMember(user.ID) - if err != nil { - log.Error("LDAP group sync: Could not add user to organization: %v", err) + err = org.AddMember(user.ID) + if err != nil { + log.Error("LDAP group sync: Could not add user to organization: %v", err) + continue + } } for _, teamName := range teamNames { team, err := org.GetTeam(teamName) @@ -38,6 +39,8 @@ func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[s } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to team [%s]", user.Name, org.Name) + } else { + continue } err = team.AddMember(user.ID) if err != nil { @@ -67,6 +70,8 @@ func removeMappedMemberships(user *models.User, ldapTeamRemove map[string][]stri } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); isMember && err == nil { log.Trace("LDAP group sync: removing user [%s] from team [%s]", user.Name, org.Name) + } else { + continue } err = team.RemoveMember(user.ID) if err != nil { @@ -74,8 +79,11 @@ func removeMappedMemberships(user *models.User, ldapTeamRemove map[string][]stri } } if remainingTeams, err := models.GetUserOrgTeams(org.ID, user.ID); err == nil && len(remainingTeams) == 0 { + // only remove organization membership when no team memberships are left for this organization if isMember, err := models.IsOrganizationMember(org.ID, user.ID); isMember && err == nil { log.Trace("LDAP group sync: removing user [%s] from organization [%s]", user.Name, org.Name) + } else { + continue } err = org.RemoveMember(user.ID) if err != nil { From edd19e2404f6cdd3a92c20d5b0e888452958c499 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Thu, 2 Sep 2021 10:02:09 +0200 Subject: [PATCH 06/19] Rename generic get keys from map function Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- modules/util/map.go | 6 +++--- services/auth/source/ldap/source_search.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/util/map.go b/modules/util/map.go index 75f49e1c91c6a..7422f5c255e1a 100644 --- a/modules/util/map.go +++ b/modules/util/map.go @@ -9,8 +9,8 @@ import ( "reflect" ) -// KeysString returns a slice of keys from a map, dict must be a map -func KeysString(dict interface{}) interface{} { +// GetKeys returns a slice of keys from a map, dict must be a map +func GetKeys(dict interface{}) interface{} { value := reflect.ValueOf(dict) valueType := value.Type() if value.Kind() == reflect.Map { @@ -22,5 +22,5 @@ func KeysString(dict interface{}) interface{} { } return resultSlice.Interface() } - panic(fmt.Sprintf("Type %s is not supported by KeysString", valueType.String())) + panic(fmt.Sprintf("Type %s is not supported by GetKeys", valueType.String())) } diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index 2808220fc4674..c9c457976a762 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -244,7 +244,7 @@ func (ls *Source) getMappedTeams(l *ldap.Conn, uid string) (map[string][]string, // unmarshall LDAP group team map from configs ldapGroupsToTeams := ls.mapLdapGroupsToTeams() // select all LDAP groups from settings - allLdapGroups := util.KeysString(ldapGroupsToTeams).([]string) + allLdapGroups := util.GetKeys(ldapGroupsToTeams).([]string) // contains LDAP config groups, which the user is a member of usersLdapGroupsToAdd := util.IntersectString(allLdapGroups, usersLdapGroups) // contains LDAP config groups, which the user is not a member of From ba93eb0dd4914a3d9fd48adca68bb43ebfa6246a Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Wed, 27 Oct 2021 17:25:01 +0200 Subject: [PATCH 07/19] Improve non-idiomatic go code Combines intersection and difference of configured ldap groups Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- modules/util/map.go | 26 --------------- modules/util/slice.go | 27 ---------------- services/auth/source/ldap/source_search.go | 37 ++++++++++------------ 3 files changed, 17 insertions(+), 73 deletions(-) delete mode 100644 modules/util/map.go delete mode 100644 modules/util/slice.go diff --git a/modules/util/map.go b/modules/util/map.go deleted file mode 100644 index 7422f5c255e1a..0000000000000 --- a/modules/util/map.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package util - -import ( - "fmt" - "reflect" -) - -// GetKeys returns a slice of keys from a map, dict must be a map -func GetKeys(dict interface{}) interface{} { - value := reflect.ValueOf(dict) - valueType := value.Type() - if value.Kind() == reflect.Map { - keys := value.MapKeys() - length := len(keys) - resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) - for i, key := range keys { - resultSlice.Index(i).Set(key) - } - return resultSlice.Interface() - } - panic(fmt.Sprintf("Type %s is not supported by GetKeys", valueType.String())) -} diff --git a/modules/util/slice.go b/modules/util/slice.go deleted file mode 100644 index 2931b4808b728..0000000000000 --- a/modules/util/slice.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package util - -// IntersectString returns the intersection of the two string slices -func IntersectString(a, b []string) []string { - var intersection []string - for _, v := range a { - if IsStringInSlice(v, b) && !IsStringInSlice(v, intersection) { - intersection = append(intersection, v) - } - } - return intersection -} - -// DifferenceString returns all elements of slice a which are not present in slice b -func DifferenceString(a, b []string) []string { - var difference []string - for _, v := range a { - if !IsStringInSlice(v, b) && !IsStringInSlice(v, difference) { - difference = append(difference, v) - } - } - return difference -} diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index b2e1d3e2ca5a4..4f71938d14e69 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -238,30 +238,27 @@ func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { return ldapGroupsToTeams } -func (ls *Source) getMappedTeams(l *ldap.Conn, uid string) (map[string][]string, map[string][]string) { - teamsToAdd := map[string][]string{} - teamsToRemove := map[string][]string{} +// getMappedMemberships : returns the organizations and teams to modify the users membership +func (ls *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string][]string, map[string][]string) { // get all LDAP group memberships for user usersLdapGroups := ls.listLdapGroupMemberships(l, uid) // unmarshall LDAP group team map from configs ldapGroupsToTeams := ls.mapLdapGroupsToTeams() - // select all LDAP groups from settings - allLdapGroups := util.GetKeys(ldapGroupsToTeams).([]string) - // contains LDAP config groups, which the user is a member of - usersLdapGroupsToAdd := util.IntersectString(allLdapGroups, usersLdapGroups) - // contains LDAP config groups, which the user is not a member of - usersLdapGroupToRemove := util.DifferenceString(allLdapGroups, usersLdapGroups) - for _, groupToAdd := range usersLdapGroupsToAdd { - for k, v := range ldapGroupsToTeams[groupToAdd] { - teamsToAdd[k] = v - } - } - for _, groupToRemove := range usersLdapGroupToRemove { - for k, v := range ldapGroupsToTeams[groupToRemove] { - teamsToRemove[k] = v + membershipsToAdd := map[string][]string{} + membershipsToRemove := map[string][]string{} + for group, memberships := range ldapGroupsToTeams { + isUserInGroup := util.IsStringInSlice(group, usersLdapGroups) + if isUserInGroup { + for org, teams := range memberships { + membershipsToAdd[org] = teams + } + } else if !isUserInGroup { + for org, teams := range memberships { + membershipsToRemove[org] = teams + } } } - return teamsToAdd, teamsToRemove + return membershipsToAdd, membershipsToRemove } // SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter @@ -451,7 +448,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul teamsToAdd := make(map[string][]string) teamsToRemove := make(map[string][]string) if ls.TeamGroupMapEnabled || ls.TeamGroupMapRemoval { - teamsToAdd, teamsToRemove = ls.getMappedTeams(l, uid) + teamsToAdd, teamsToRemove = ls.getMappedMemberships(l, uid) } return &SearchResult{ @@ -534,7 +531,7 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) { if ls.UserUID == "dn" || ls.UserUID == "DN" { userAttributeListedInGroup = v.DN } - teamsToAdd, teamsToRemove = ls.getMappedTeams(l, userAttributeListedInGroup) + teamsToAdd, teamsToRemove = ls.getMappedMemberships(l, userAttributeListedInGroup) } result[i] = &SearchResult{ Username: v.GetAttributeValue(ls.AttributeUsername), From 4d864b8e75ecbb24491419b2bba8ded737c6ca6a Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 2 Nov 2021 12:40:34 +0100 Subject: [PATCH 08/19] Add cache for teams and orgs Cache is populated during external user sync Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- .../auth/source/ldap/source_authenticate.go | 12 ++-- .../auth/source/ldap/source_group_sync.go | 65 ++++++++++++------- services/auth/source/ldap/source_sync.go | 4 +- 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index bc884ce0d37fc..f652702ef8e87 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -56,12 +56,14 @@ func (source *Source) Authenticate(user *models.User, userName, password string) } if user != nil { + if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { + orgCache := make(map[string]*models.User) + teamCache := make(map[string]*models.Team) + source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) + } if isAttributeSSHPublicKeySet && models.SynchronizePublicKeys(user, source.loginSource, sr.SSHPublicKey) { return user, models.RewriteAllPublicKeys() } - if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { - source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove) - } return user, nil } @@ -101,7 +103,9 @@ func (source *Source) Authenticate(user *models.User, userName, password string) _ = user.UploadAvatar(sr.Avatar) } if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { - source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove) + orgCache := make(map[string]*models.User) + teamCache := make(map[string]*models.Team) + source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) } return user, err diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index c62ad346ec200..f2ab4432a3366 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -10,17 +10,22 @@ import ( ) // SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships -func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[string][]string, ldapTeamRemove map[string][]string) { +func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[string][]string, ldapTeamRemove map[string][]string, orgCache map[string]*models.User, teamCache map[string]*models.Team) { + var err error if source.TeamGroupMapRemoval { // when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships - removeMappedMemberships(user, ldapTeamRemove) + removeMappedMemberships(user, ldapTeamRemove, orgCache, teamCache) } for orgName, teamNames := range ldapTeamAdd { - org, err := models.GetOrgByName(orgName) - if err != nil { - // organization must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) - continue + org, ok := orgCache[orgName] + if !ok { + org, err = models.GetOrgByName(orgName) + if err != nil { + // organization must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) + continue + } + orgCache[orgName] = org } if isMember, err := models.IsOrganizationMember(org.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to organization [%s]", user.Name, org.Name) @@ -31,18 +36,22 @@ func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[s } } for _, teamName := range teamNames { - team, err := org.GetTeam(teamName) - if err != nil { - // team must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) - continue + team, ok := teamCache[orgName+teamName] + if !ok { + team, err = org.GetTeam(teamName) + if err != nil { + // team must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + continue + } + teamCache[orgName+teamName] = team } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); !isMember && err == nil { log.Trace("LDAP group sync: adding user [%s] to team [%s]", user.Name, org.Name) } else { continue } - err = team.AddMember(user.ID) + err := team.AddMember(user.ID) if err != nil { log.Error("LDAP group sync: Could not add user to team: %v", err) } @@ -53,21 +62,29 @@ func (source *Source) SyncLdapGroupsToTeams(user *models.User, ldapTeamAdd map[s // remove membership to organizations/teams if user is not member of corresponding LDAP group // e.g. lets assume user is member of LDAP group "x", but LDAP group team map contains LDAP groups "x" and "y" // then users membership gets removed for all organizations/teams mapped by LDAP group "y" -func removeMappedMemberships(user *models.User, ldapTeamRemove map[string][]string) { +func removeMappedMemberships(user *models.User, ldapTeamRemove map[string][]string, orgCache map[string]*models.User, teamCache map[string]*models.Team) { + var err error for orgName, teamNames := range ldapTeamRemove { - org, err := models.GetOrgByName(orgName) - if err != nil { - // organization must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) - continue - } - for _, teamName := range teamNames { - team, err := org.GetTeam(teamName) + org, ok := orgCache[orgName] + if !ok { + org, err = models.GetOrgByName(orgName) if err != nil { - // team must must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + // organization must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) continue } + orgCache[orgName] = org + } + for _, teamName := range teamNames { + team, ok := teamCache[orgName+teamName] + if !ok { + team, err = org.GetTeam(teamName) + if err != nil { + // team must must be created before LDAP group sync + log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + continue + } + } if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); isMember && err == nil { log.Trace("LDAP group sync: removing user [%s] from team [%s]", user.Name, org.Name) } else { diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index ba3ad19fece32..bacc52075f644 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -58,6 +58,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { }) userPos := 0 + orgCache := make(map[string]*models.User) + teamCache := make(map[string]*models.Team) for _, su := range sr { select { @@ -166,7 +168,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } // Synchronize LDAP groups with organization and team memberships if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { - source.SyncLdapGroupsToTeams(usr, su.LdapTeamAdd, su.LdapTeamRemove) + source.SyncLdapGroupsToTeams(usr, su.LdapTeamAdd, su.LdapTeamRemove, orgCache, teamCache) } } From 1849924933f069d38b0ae7e262f3b4b44dce0e73 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Thu, 11 Nov 2021 12:48:16 +0100 Subject: [PATCH 09/19] Fix cli command flag and checkbox listener Co-authored-by: Sven Seeberg Co-authored-by: Giuliano Mele --- cmd/admin_auth_ldap.go | 5 +++-- web_src/js/features/admin-common.js | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 3795b406de807..51d9e16d3afcc 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -102,8 +102,8 @@ var ( Name: "team-group-map", Usage: "Map of LDAP groups to teams.", }, - cli.StringFlag{ - Name: "team-group-map-force", + cli.BoolFlag{ + Name: "team-group-map-removal", Usage: "Force synchronization of mapped LDAP groups to teams.", }, } @@ -269,6 +269,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error { config.SkipLocalTwoFA = c.Bool("skip-local-2fa") } if c.IsSet("team-group-map") { + config.TeamGroupMapEnabled = c.Bool("team-group-map") config.TeamGroupMap = c.String("team-group-map") } if c.IsSet("team-group-map-removal") { diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js index b01aa8b989178..e0039b27624f9 100644 --- a/web_src/js/features/admin-common.js +++ b/web_src/js/features/admin-common.js @@ -159,6 +159,8 @@ export function initAdminCommon() { $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); $('#groups_enabled').on('change', onVerifyGroupMembershipChange); + $('#team_group_map_enabled').on('change', onVerifyGroupMembershipChange); + $('#team_group_map_removal').on('change', onVerifyGroupMembershipChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { @@ -166,6 +168,8 @@ export function initAdminCommon() { if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); $('#groups_enabled').on('change', onVerifyGroupMembershipChange); + $('#team_group_map_enabled').on('change', onVerifyGroupMembershipChange); + $('#team_group_map_removal').on('change', onVerifyGroupMembershipChange); onVerifyGroupMembershipChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); From 6d21c2b04d2a41af196efed32aabd38788250651 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 14 Dec 2021 12:50:25 +0100 Subject: [PATCH 10/19] Set log level to warning for missing orgs/teams --- services/auth/source/ldap/source_group_sync.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index 8403325f6ed04..99f175b5dc758 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -23,7 +23,7 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd m org, err = models.GetOrgByName(orgName) if err != nil { // organization must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) + log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err) continue } orgCache[orgName] = org @@ -42,7 +42,7 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd m team, err = org.GetTeam(teamName) if err != nil { // team must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + log.Warn("LDAP group sync: Could not find team %s: %v", teamName, err) continue } teamCache[orgName+teamName] = team @@ -71,7 +71,7 @@ func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][] org, err = models.GetOrgByName(orgName) if err != nil { // organization must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find organisation %s: %v", orgName, err) + log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err) continue } orgCache[orgName] = org @@ -82,7 +82,7 @@ func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][] team, err = org.GetTeam(teamName) if err != nil { // team must must be created before LDAP group sync - log.Debug("LDAP group sync: Could not find team %s: %v", teamName, err) + log.Warn("LDAP group sync: Could not find team %s: %v", teamName, err) continue } } From f8d7a390e0e3aa6f99a6f7cd16a3d82827eeae66 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 14 Dec 2021 13:09:37 +0100 Subject: [PATCH 11/19] Remove redundant check remaining team memberships --- services/auth/source/ldap/source_group_sync.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index 99f175b5dc758..dc0f22e59d155 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -96,19 +96,5 @@ func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][] log.Error("LDAP group sync: Could not remove user from team: %v", err) } } - if remainingTeams, err := models.GetUserOrgTeams(org.ID, user.ID); err == nil && len(remainingTeams) == 0 { - // only remove organization membership when no team memberships are left for this organization - if isMember, err := models.IsOrganizationMember(org.ID, user.ID); isMember && err == nil { - log.Trace("LDAP group sync: removing user [%s] from organization [%s]", user.Name, org.Name) - } else { - continue - } - err = org.RemoveMember(user.ID) - if err != nil { - log.Error("LDAP group sync: Could not remove user from organization: %v", err) - } - } else if err != nil { - log.Error("LDAP group sync: Could not find users [id: %d] teams for given organization [%s]", user.ID, org.Name) - } } } From c03bcb7b330711ef30be69dcc8f43b32ec5d1157 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 14 Dec 2021 13:46:16 +0100 Subject: [PATCH 12/19] Fix integration tests --- integrations/auth_ldap_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index cfd48d37ce02a..0a4cf92e9f827 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -12,7 +12,8 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/services/auth" "github.com/stretchr/testify/assert" @@ -324,10 +325,13 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { assert.NoError(t, err) auth.SyncExternalUsers(context.Background(), true) for _, gitLDAPUser := range gitLDAPUsers { - user := db.AssertExistsAndLoadBean(t, &models.User{ + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUser.UserName, - }).(*models.User) - usersOrgs, err := models.GetOrgsByUserID(user.ID, true) + }).(*user_model.User) + usersOrgs, err := models.FindOrgs(models.FindOrgOptions{ + UserID: user.ID, + IncludePrivate: true, + }) assert.NoError(t, err) allOrgTeams, err := models.GetUserOrgTeams(org.ID, user.ID) assert.NoError(t, err) @@ -365,9 +369,9 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { team, err := models.GetTeam(org.ID, "team11") assert.NoError(t, err) loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password) - user := db.AssertExistsAndLoadBean(t, &models.User{ + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUsers[0].UserName, - }).(*models.User) + }).(*user_model.User) err = org.AddMember(user.ID) assert.NoError(t, err) err = team.AddMember(user.ID) From 675d64dbe60b290d6ede9c870add3c506a1bf271 Mon Sep 17 00:00:00 2001 From: Giuliano Mele Date: Tue, 14 Dec 2021 15:07:07 +0100 Subject: [PATCH 13/19] Disable group mapping checkbox on LDAP removal --- web_src/js/features/admin-common.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js index e0039b27624f9..b551d5403d34f 100644 --- a/web_src/js/features/admin-common.js +++ b/web_src/js/features/admin-common.js @@ -89,7 +89,14 @@ export function initAdminCommon() { } } - function onVerifyGroupMembershipChange() { + function onVerifyGroupMembershipChange(event) { + if ($(event.target).attr('id') === 'team_group_map_removal' && $('#team_group_map_removal').is(':checked')) { + $('#team_group_map_enabled').prop('checked', true); + $('#team_group_map_enabled').prop('disabled', true); + } else if ($(event.target).attr('id') === 'team_group_map_removal') { + $('#team_group_map_enabled').prop('checked', false); + $('#team_group_map_enabled').prop('disabled', false); + } if ($('#groups_enabled').is(':checked') || $('#team_group_map_enabled').is(':checked') || $('#team_group_map_removal').is(':checked')) { $('#groups_enabled_change').show(); } else { @@ -158,18 +165,14 @@ export function initAdminCommon() { $('#use_paged_search').on('change', onUsePagedSearchChange); $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); - $('#team_group_map_enabled').on('change', onVerifyGroupMembershipChange); - $('#team_group_map_removal').on('change', onVerifyGroupMembershipChange); + $('.ldap_group_options').on('change', onVerifyGroupMembershipChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { const authType = $('#auth_type').val(); if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); - $('#team_group_map_enabled').on('change', onVerifyGroupMembershipChange); - $('#team_group_map_removal').on('change', onVerifyGroupMembershipChange); + $('.ldap_group_options').on('change', onVerifyGroupMembershipChange); onVerifyGroupMembershipChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); From a75516dc4605eab5f941765f3f3f0648856626e9 Mon Sep 17 00:00:00 2001 From: Gitea Date: Sat, 22 Jan 2022 11:50:56 +0100 Subject: [PATCH 14/19] Run make fmt --- services/auth/source/ldap/source_group_sync.go | 2 +- services/auth/source/ldap/source_search.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index dc0f22e59d155..0b62f8b18615b 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -11,7 +11,7 @@ import ( ) // SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships -func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd map[string][]string, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) { +func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) { var err error if source.TeamGroupMapRemoval { // when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index 4f71938d14e69..ddb55c36cf160 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -199,7 +199,7 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool { // List all group memberships of a user func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { var ldapGroups []string - var groupFilter = fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) + groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) result, err := l.Search(ldap.NewSearchRequest( ls.GroupDN, ldap.ScopeWholeSubtree, From 0d402cc63e36b9f93b438c11df951036172bbee7 Mon Sep 17 00:00:00 2001 From: Sven Seeberg Date: Sat, 22 Jan 2022 12:10:15 +0100 Subject: [PATCH 15/19] use kebap case for CSS classes --- templates/admin/auth/edit.tmpl | 6 +++--- templates/admin/auth/source/ldap.tmpl | 6 +++--- web_src/js/features/admin-common.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 40974a56f2c1e..167065e06bc37 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -111,19 +111,19 @@
- +
- +
- +
diff --git a/templates/admin/auth/source/ldap.tmpl b/templates/admin/auth/source/ldap.tmpl index 7115474fa4341..36adcd8a7ff4d 100644 --- a/templates/admin/auth/source/ldap.tmpl +++ b/templates/admin/auth/source/ldap.tmpl @@ -82,19 +82,19 @@
- +
- +
- +
diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js index b551d5403d34f..b2ec142de80fd 100644 --- a/web_src/js/features/admin-common.js +++ b/web_src/js/features/admin-common.js @@ -165,14 +165,14 @@ export function initAdminCommon() { $('#use_paged_search').on('change', onUsePagedSearchChange); $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); - $('.ldap_group_options').on('change', onVerifyGroupMembershipChange); + $('.ldap-group-options').on('change', onVerifyGroupMembershipChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { const authType = $('#auth_type').val(); if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); - $('.ldap_group_options').on('change', onVerifyGroupMembershipChange); + $('.ldap-group-options').on('change', onVerifyGroupMembershipChange); onVerifyGroupMembershipChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); From 6ef197e4bb7c1d9f9a1755ed84fd8ce7b10da3ba Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 8 Feb 2022 23:58:59 +0800 Subject: [PATCH 16/19] refactor --- cmd/admin_auth_ldap.go | 16 ------ integrations/auth_ldap_test.go | 23 ++++---- options/locale/locale_en-US.ini | 9 ++-- routers/web/admin/auths.go | 5 +- services/auth/source/ldap/source.go | 5 +- .../auth/source/ldap/source_authenticate.go | 4 +- .../auth/source/ldap/source_group_sync.go | 2 +- services/auth/source/ldap/source_search.go | 6 +-- services/auth/source/ldap/source_sync.go | 2 +- services/forms/auth_form.go | 5 +- templates/admin/auth/edit.tmpl | 52 +++++++++---------- templates/admin/auth/source/ldap.tmpl | 51 ++++++++---------- web_src/js/features/admin-common.js | 33 +++--------- 13 files changed, 80 insertions(+), 133 deletions(-) diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index fd261f7c252dc..ec86b2c671d47 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -98,14 +98,6 @@ var ( Name: "avatar-attribute", Usage: "The attribute of the user’s LDAP record containing the user’s avatar.", }, - cli.StringFlag{ - Name: "team-group-map", - Usage: "Map of LDAP groups to teams.", - }, - cli.BoolFlag{ - Name: "team-group-map-removal", - Usage: "Force synchronization of mapped LDAP groups to teams.", - }, } ldapBindDnCLIFlags = append(commonLdapCLIFlags, @@ -268,14 +260,6 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error { if c.IsSet("skip-local-2fa") { config.SkipLocalTwoFA = c.Bool("skip-local-2fa") } - if c.IsSet("team-group-map") { - config.TeamGroupMapEnabled = c.Bool("team-group-map") - config.TeamGroupMap = c.String("team-group-map") - } - if c.IsSet("team-group-map-removal") { - config.TeamGroupMapRemoval = c.Bool("team-group-map-removal") - } - return nil } diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index 0a4cf92e9f827..c68e418523539 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -101,13 +101,11 @@ func getLDAPServerHost() string { } func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string, groupMapParams ...string) { - teamGroupMapEnabled := "off" - teamGroupMapRemoval := "off" - teamGroupMap := "" - if len(groupMapParams) == 3 { - teamGroupMapEnabled = groupMapParams[0] - teamGroupMapRemoval = groupMapParams[1] - teamGroupMap = groupMapParams[2] + groupTeamMapRemoval := "off" + groupTeamMap := "" + if len(groupMapParams) == 2 { + groupTeamMapRemoval = groupMapParams[0] + groupTeamMap = groupMapParams[1] } session := loginUser(t, "user1") csrf := GetCSRF(t, session, "/admin/auths/new") @@ -130,12 +128,11 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string, groupMapParams ...s "attribute_ssh_public_key": sshKeyAttribute, "is_sync_enabled": "on", "is_active": "on", - "team_group_map_enabled": teamGroupMapEnabled, - "team_group_map_removal": teamGroupMapRemoval, "group_dn": "ou=people,dc=planetexpress,dc=com", "group_member_uid": "member", + "group_team_map": groupTeamMap, + "group_team_map_removal": groupTeamMapRemoval, "user_uid": "DN", - "team_group_map": teamGroupMap, }) session.MakeRequest(t, req, http.StatusFound) } @@ -318,7 +315,7 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { return } defer prepareTestEnv(t)() - addAuthSourceLDAP(t, "", "on", "on", "{\"cn=ship_crew,ou=people,dc=planetexpress,dc=com\":{\"org26\": [\"team11\"]},\"cn=admin_staff,ou=people,dc=planetexpress,dc=com\": {\"non-existent\": [\"non-existent\"]}}") + addAuthSourceLDAP(t, "", "on", `{"cn=ship_crew,ou=people,dc=planetexpress,dc=com":{"org26": ["team11"]},"cn=admin_staff,ou=people,dc=planetexpress,dc=com": {"non-existent": ["non-existent"]}}`) org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") @@ -363,7 +360,7 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) { return } defer prepareTestEnv(t)() - addAuthSourceLDAP(t, "", "on", "on", "{\"cn=dispatch,ou=people,dc=planetexpress,dc=com\": {\"org26\": [\"team11\"]}}") + addAuthSourceLDAP(t, "", "on", `{"cn=dispatch,ou=people,dc=planetexpress,dc=com": {"org26": ["team11"]}}`) org, err := models.GetOrgByName("org26") assert.NoError(t, err) team, err := models.GetTeam(org.ID, "team11") @@ -399,7 +396,7 @@ func TestBrokenLDAPMapUserSignin(t *testing.T) { return } defer prepareTestEnv(t)() - addAuthSourceLDAP(t, "", "on", "on", "{\"NOT_A_VALID_JSON\"[\"MISSING_DOUBLE_POINT\"]}") + addAuthSourceLDAP(t, "", "on", `{"NOT_A_VALID_JSON"["MISSING_DOUBLE_POINT"]}`) u := gitLDAPUsers[0] diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a544388d403a6..44116e592374e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2561,14 +2561,13 @@ auths.filter = User Filter auths.admin_filter = Admin Filter auths.restricted_filter = Restricted Filter auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ('*') to set all users that do not match Admin Filter as restricted. -auths.verify_group_membership = Verify group membership in LDAP +auths.verify_group_membership = Verify group membership in LDAP (leave the filter empty to skip) auths.group_search_base = Group Search Base DN -auths.valid_groups_filter = Valid Groups Filter auths.group_attribute_list_users = Group Attribute Containing List Of Users auths.user_attribute_in_group = User Attribute Listed In Group -auths.team_group_map = Map LDAP groups to Organization teams -auths.team_group_map_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group -auths.team_group_map_enabled = Enable mapping LDAP groups to gitea organizations teams +auths.map_group_to_team = Map LDAP groups to Organization teams (leave the filter empty to skip) +auths.map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group +auths.enable_ldap_groups = Enable LDAP groups auths.ms_ad_sa = MS AD Search Attributes auths.smtp_auth = SMTP Authentication Type auths.smtphost = SMTP Host diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 9bde7c687383f..748f2e7a8b8bf 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -145,13 +145,12 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source { GroupDN: form.GroupDN, GroupFilter: form.GroupFilter, GroupMemberUID: form.GroupMemberUID, + GroupTeamMap: form.GroupTeamMap, + GroupTeamMapRemoval: form.GroupTeamMapRemoval, UserUID: form.UserUID, AdminFilter: form.AdminFilter, RestrictedFilter: form.RestrictedFilter, AllowDeactivateAll: form.AllowDeactivateAll, - TeamGroupMap: form.TeamGroupMap, - TeamGroupMapRemoval: form.TeamGroupMapRemoval, - TeamGroupMapEnabled: form.TeamGroupMapEnabled, Enabled: true, SkipLocalTwoFA: form.SkipLocalTwoFA, } diff --git a/services/auth/source/ldap/source.go b/services/auth/source/ldap/source.go index d6f62782cf521..ad97e2dd499be 100644 --- a/services/auth/source/ldap/source.go +++ b/services/auth/source/ldap/source.go @@ -52,11 +52,10 @@ type Source struct { GroupDN string // Group Search Base GroupFilter string // Group Name Filter GroupMemberUID string // Group Attribute containing array of UserUID + GroupTeamMap string // Map LDAP groups to teams + GroupTeamMapRemoval bool // Remove user from teams which are synchronized and user is not a member of the corresponding LDAP group UserUID string // User Attribute listed in Group SkipLocalTwoFA bool `json:",omitempty"` // Skip Local 2fa for users authenticated with this source - TeamGroupMap string // Map LDAP groups to teams - TeamGroupMapRemoval bool // Remove user from teams which are synchronized and user is not a member of the corresponding LDAP group - TeamGroupMapEnabled bool // if LDAP groups mapping to gitea organizations teams is enabled // reference to the authSource authSource *auth.Source diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index 179d12fa27528..e804e32e845db 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -60,7 +60,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str } if user != nil { - if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { + if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { orgCache := make(map[string]*models.Organization) teamCache := make(map[string]*models.Team) source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) @@ -106,7 +106,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str if err == nil && len(source.AttributeAvatar) > 0 { _ = user_service.UploadAvatar(user, sr.Avatar) } - if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { + if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { orgCache := make(map[string]*models.Organization) teamCache := make(map[string]*models.Team) source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache) diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go index 0b62f8b18615b..7c62af705e04a 100644 --- a/services/auth/source/ldap/source_group_sync.go +++ b/services/auth/source/ldap/source_group_sync.go @@ -13,7 +13,7 @@ import ( // SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) { var err error - if source.TeamGroupMapRemoval { + if source.GroupsEnabled && source.GroupTeamMapRemoval { // when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships removeMappedMemberships(user, ldapTeamRemove, orgCache, teamCache) } diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index ddb55c36cf160..0be5bd747c094 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -230,7 +230,7 @@ func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { // parse LDAP groups and return map of ldap groups to organizations teams func (ls *Source) mapLdapGroupsToTeams() map[string]map[string][]string { ldapGroupsToTeams := make(map[string]map[string][]string) - err := json.Unmarshal([]byte(ls.TeamGroupMap), &ldapGroupsToTeams) + err := json.Unmarshal([]byte(ls.GroupTeamMap), &ldapGroupsToTeams) if err != nil { log.Error("Failed to unmarshall LDAP teams map: %v", err) return ldapGroupsToTeams @@ -447,7 +447,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul teamsToAdd := make(map[string][]string) teamsToRemove := make(map[string][]string) - if ls.TeamGroupMapEnabled || ls.TeamGroupMapRemoval { + if ls.GroupsEnabled && (ls.GroupTeamMap != "" || ls.GroupTeamMapRemoval) { teamsToAdd, teamsToRemove = ls.getMappedMemberships(l, uid) } @@ -526,7 +526,7 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) { for i, v := range sr.Entries { teamsToAdd := make(map[string][]string) teamsToRemove := make(map[string][]string) - if ls.TeamGroupMapEnabled || ls.TeamGroupMapRemoval { + if ls.GroupsEnabled && (ls.GroupTeamMap != "" || ls.GroupTeamMapRemoval) { userAttributeListedInGroup := v.GetAttributeValue(ls.UserUID) if ls.UserUID == "dn" || ls.UserUID == "DN" { userAttributeListedInGroup = v.DN diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 4f4f001410bdf..74a62ce4e72dc 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -170,7 +170,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } } // Synchronize LDAP groups with organization and team memberships - if source.TeamGroupMapEnabled || source.TeamGroupMapRemoval { + if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { source.SyncLdapGroupsToTeams(usr, su.LdapTeamAdd, su.LdapTeamRemove, orgCache, teamCache) } } diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 2b7f6497a509b..7e7c75675299b 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -79,9 +79,8 @@ type AuthenticationForm struct { SSPIStripDomainNames bool SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"` SSPIDefaultLanguage string - TeamGroupMap string - TeamGroupMapRemoval bool - TeamGroupMapEnabled bool + GroupTeamMap string + GroupTeamMapRemoval bool } // Validate validates fields diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 6e0796dffc929..31c87597f0551 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -108,47 +108,43 @@
+ + +
- - + +
-
-
- - +
+
+ +
-
-
-
- - +
+ +
-
-
- - + +
- - + +
- - + + +
+
+ +
-
-
-
- - -
-
- -
+ + {{if .Source.IsLDAP}}
diff --git a/templates/admin/auth/source/ldap.tmpl b/templates/admin/auth/source/ldap.tmpl index 36adcd8a7ff4d..afdfbadd6518e 100644 --- a/templates/admin/auth/source/ldap.tmpl +++ b/templates/admin/auth/source/ldap.tmpl @@ -79,47 +79,42 @@
+ +
- - + +
-
-
- - +
+
+ +
-
-
-
- - +
+ +
-
-
- - + +
- - + +
- - + + +
+
+ +
-
-
-
- - -
-
- -
+ +
diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js index 2d151f39226a2..2438fcf62b465 100644 --- a/web_src/js/features/admin-common.js +++ b/web_src/js/features/admin-common.js @@ -91,29 +91,8 @@ export function initAdminCommon() { } } - function onVerifyGroupMembershipChange(event) { - if ($(event.target).attr('id') === 'team_group_map_removal' && $('#team_group_map_removal').is(':checked')) { - $('#team_group_map_enabled').prop('checked', true); - $('#team_group_map_enabled').prop('disabled', true); - } else if ($(event.target).attr('id') === 'team_group_map_removal') { - $('#team_group_map_enabled').prop('checked', false); - $('#team_group_map_enabled').prop('disabled', false); - } - if ($('#groups_enabled').is(':checked') || $('#team_group_map_enabled').is(':checked') || $('#team_group_map_removal').is(':checked')) { - $('#groups_enabled_change').show(); - } else { - $('#groups_enabled_change').hide(); - } - if ($('#groups_enabled').is(':checked')) { - $('#group_filter_field').show(); - } else { - $('#group_filter_field').hide(); - } - if ($('#team_group_map_enabled').is(':checked') || $('#team_group_map_removal').is(':checked')) { - $('#team_group_map_field').show(); - } else { - $('#team_group_map_field').hide(); - } + function onEnableLdapGroupsChange() { + $('#ldap-group-options').toggle($('.js-ldap-group-toggle').is(':checked')); } // New authentication @@ -156,7 +135,7 @@ export function initAdminCommon() { } if (authType === '2' || authType === '5') { onSecurityProtocolChange(); - onVerifyGroupMembershipChange(); + onEnableLdapGroupsChange(); } if (authType === '2') { onUsePagedSearchChange(); @@ -167,15 +146,15 @@ export function initAdminCommon() { $('#use_paged_search').on('change', onUsePagedSearchChange); $('#oauth2_provider').on('change', () => onOAuth2Change(true)); $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); - $('.ldap-group-options').on('change', onVerifyGroupMembershipChange); + $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); } // Edit authentication if ($('.admin.edit.authentication').length > 0) { const authType = $('#auth_type').val(); if (authType === '2' || authType === '5') { $('#security_protocol').on('change', onSecurityProtocolChange); - $('.ldap-group-options').on('change', onVerifyGroupMembershipChange); - onVerifyGroupMembershipChange(); + $('.js-ldap-group-toggle').on('change', onEnableLdapGroupsChange); + onEnableLdapGroupsChange(); if (authType === '2') { $('#use_paged_search').on('change', onUsePagedSearchChange); } From d01e377c7600a4e1cb4327c84944dd29c5656894 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 10 Feb 2022 16:36:04 +0800 Subject: [PATCH 17/19] fix lint --- services/auth/source/ldap/source_search.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index 0be5bd747c094..57b1ac7e6a120 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -7,12 +7,12 @@ package ldap import ( "crypto/tls" - "encoding/json" "fmt" "net" "strconv" "strings" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" @@ -28,7 +28,7 @@ type SearchResult struct { SSHPublicKey []string // SSH Public Key IsAdmin bool // if user is administrator IsRestricted bool // if user is restricted - LowerName string // Lowername + LowerName string // LowerName Avatar []byte LdapTeamAdd map[string][]string // organizations teams to add LdapTeamRemove map[string][]string // organizations teams to remove From 82d0cb3f01c235e9512a1b412b0f66d281a03e54 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 10 Feb 2022 17:05:58 +0800 Subject: [PATCH 18/19] try to fix unit test --- integrations/auth_ldap_test.go | 1 + options/locale/locale_en-US.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go index c68e418523539..ef0fafc93de4e 100644 --- a/integrations/auth_ldap_test.go +++ b/integrations/auth_ldap_test.go @@ -128,6 +128,7 @@ func addAuthSourceLDAP(t *testing.T, sshKeyAttribute string, groupMapParams ...s "attribute_ssh_public_key": sshKeyAttribute, "is_sync_enabled": "on", "is_active": "on", + "groups_enabled": "on", "group_dn": "ou=people,dc=planetexpress,dc=com", "group_member_uid": "member", "group_team_map": groupTeamMap, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d4eb9497d833c..3d3d3197ac7cc 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2584,7 +2584,7 @@ auths.verify_group_membership = Verify group membership in LDAP (leave the filte auths.group_search_base = Group Search Base DN auths.group_attribute_list_users = Group Attribute Containing List Of Users auths.user_attribute_in_group = User Attribute Listed In Group -auths.map_group_to_team = Map LDAP groups to Organization teams (leave the filter empty to skip) +auths.map_group_to_team = Map LDAP groups to Organization teams (leave the field empty to skip) auths.map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding LDAP group auths.enable_ldap_groups = Enable LDAP groups auths.ms_ad_sa = MS AD Search Attributes From 8f0b40a7fc2db8af7fd41c4e29e6b3ef809c55fb Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 11 Feb 2022 20:44:30 +0800 Subject: [PATCH 19/19] fix unit test --- services/auth/source/ldap/source_search.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index 57b1ac7e6a120..f2b940cabe026 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -382,7 +382,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul } // Check group membership - if ls.GroupsEnabled { + if ls.GroupsEnabled && ls.GroupFilter != "" { groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter) if !ok { return nil