<commit_before>package teams

import (
	"fmt"
	"sort"

	"github.com/keybase/client/go/kbfs"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type implicitTeamConflict struct {
	// Note this TeamID is not validated by LookupImplicitTeam. Be aware of server trust.
	TeamID       keybase1.TeamID `json:"team_id"`
	Generation   int             `json:"generation"`
	ConflictDate string          `json:"conflict_date"`
}

func (i *implicitTeamConflict) parse() (*keybase1.ImplicitTeamConflictInfo, error) {
	return libkb.ParseImplicitTeamDisplayNameSuffix(fmt.Sprintf("(conflicted copy %s #%d)", i.ConflictDate, i.Generation))
}

type implicitTeam struct {
	TeamID      keybase1.TeamID        `json:"team_id"`
	DisplayName string                 `json:"display_name"`
	Private     bool                   `json:"is_private"`
	Conflicts   []implicitTeamConflict `json:"conflicts,omitempty"`
	Status      libkb.AppStatus        `json:"status"`
}

func (i *implicitTeam) GetAppStatus() *libkb.AppStatus {
	return &i.Status
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupImplicitTeam(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, err error) {

	teamID, teamName, impTeamName, tlfID, _, err = LookupImplicitTeamAndConflicts(ctx, g, displayName, public)
	return teamID, teamName, impTeamName, tlfID, err
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupImplicitTeamAndConflicts(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, conflicts []keybase1.ImplicitTeamConflictInfo, err error) {
	impName, err := ResolveImplicitTeamDisplayName(ctx, g, displayName, public)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	return lookupImplicitTeamAndConflicts(ctx, g, displayName, impName)
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Does not resolve social assertions.
// preResolveDisplayName is used for logging and errors
func lookupImplicitTeamAndConflicts(ctx context.Context, g *libkb.GlobalContext,
	preResolveDisplayName string, impTeamNameInput keybase1.ImplicitTeamDisplayName) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, conflicts []keybase1.ImplicitTeamConflictInfo, err error) {

	defer g.CTraceTimed(ctx, fmt.Sprintf("lookupImplicitTeamAndConflicts(%v)", preResolveDisplayName), func() error { return err })()

	impTeamName = impTeamNameInput

	// Use a copy without the conflict info to hit the api endpoint
	var impTeamNameWithoutConflict keybase1.ImplicitTeamDisplayName
	impTeamNameWithoutConflict = impTeamName
	impTeamNameWithoutConflict.ConflictInfo = nil
	lookupNameWithoutConflict, err := FormatImplicitTeamDisplayName(ctx, g, impTeamNameWithoutConflict)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}

	arg := libkb.NewAPIArgWithNetContext(ctx, "team/implicit")
	arg.SessionType = libkb.APISessionTypeOPTIONAL
	arg.Args = libkb.HTTPArgs{
		"display_name": libkb.S{Val: lookupNameWithoutConflict},
		"public":       libkb.B{Val: impTeamName.IsPublic},
	}
	var imp implicitTeam
	if err = g.API.GetDecode(arg, &imp); err != nil {
		if aerr, ok := err.(libkb.AppStatusError); ok {
			code := keybase1.StatusCode(aerr.Code)
			switch code {
			case keybase1.StatusCode_SCTeamReadError:
				return teamID, teamName, impTeamName, tlfID, conflicts, NewTeamDoesNotExistError(
					impTeamName.IsPublic, preResolveDisplayName)
			case keybase1.StatusCode_SCTeamProvisionalCanKey, keybase1.StatusCode_SCTeamProvisionalCannotKey:
				return teamID, teamName, impTeamName, tlfID, conflicts, libkb.NewTeamProvisionalError(
					(code == keybase1.StatusCode_SCTeamProvisionalCanKey), impTeamName.IsPublic, preResolveDisplayName)
			}
		}
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	if len(imp.Conflicts) > 0 {
		g.Log.CDebugf(ctx, "LookupImplicitTeam found %v conflicts", len(imp.Conflicts))
	}
	// We will use this team. Changed later if we selected a conflict.
	var foundSelectedConflict bool
	teamID = imp.TeamID
	for _, conflict := range imp.Conflicts {
		conflictInfo, err := conflict.parse()
		if err != nil {
			// warn, don't fail
			g.Log.CWarningf(ctx, "LookupImplicitTeam got conflict suffix: %v", err)
		} else {
			conflicts = append(conflicts, *conflictInfo)
		}
		if impTeamName.ConflictInfo != nil {
			match := libkb.FormatImplicitTeamDisplayNameSuffix(*impTeamName.ConflictInfo) == libkb.FormatImplicitTeamDisplayNameSuffix(*conflictInfo)
			if match {
				teamID = conflict.TeamID
				foundSelectedConflict = true
			}
		}
	}
	if impTeamName.ConflictInfo != nil && !foundSelectedConflict {
		// We got the team but didn't find the specific conflict requested.
		return teamID, teamName, impTeamName, tlfID, conflicts, NewTeamDoesNotExistError(
			impTeamName.IsPublic, "could not find team with suffix: %v", preResolveDisplayName)
	}
	team, err := Load(ctx, g, keybase1.LoadTeamArg{
		ID:          imp.TeamID,
		Public:      impTeamName.IsPublic,
		ForceRepoll: true,
	})
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}

	// Check the display names. This is how we make sure the server returned a team with the right members.
	teamDisplayName, err := team.ImplicitTeamDisplayNameString(ctx)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	referenceImpName, err := FormatImplicitTeamDisplayName(ctx, g, impTeamNameWithoutConflict)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	if teamDisplayName != referenceImpName {
		return teamID, teamName, impTeamName, tlfID, conflicts, fmt.Errorf("implicit team name mismatch: %s != %s",
			teamDisplayName, referenceImpName)
	}
	if team.IsPublic() != impTeamName.IsPublic {
		return teamID, teamName, impTeamName, tlfID, conflicts, fmt.Errorf("implicit team public-ness mismatch: %v != %v", team.IsPublic(), impTeamName.IsPublic)
	}

	tlfID = team.KBFSTLFID()

	return teamID, team.Name(), impTeamName, tlfID, conflicts, nil
}

// Lookup or create an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupOrCreateImplicitTeam(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (res keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, err error) {
	defer g.CTraceTimed(ctx, fmt.Sprintf("LookupOrCreateImplicitTeam(%v)", displayName),
		func() error { return err })()
	lookupName, err := ResolveImplicitTeamDisplayName(ctx, g, displayName, public)<commit_msg>maybe fix CI test that lookups up conflict info suffixes (#10151)<commit_after>package teams

import (
	"fmt"
	"sort"

	"github.com/keybase/client/go/kbfs"
	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type implicitTeamConflict struct {
	// Note this TeamID is not validated by LookupImplicitTeam. Be aware of server trust.
	TeamID       keybase1.TeamID `json:"team_id"`
	Generation   int             `json:"generation"`
	ConflictDate string          `json:"conflict_date"`
}

func (i *implicitTeamConflict) parse() (*keybase1.ImplicitTeamConflictInfo, error) {
	return libkb.ParseImplicitTeamDisplayNameSuffix(fmt.Sprintf("(conflicted copy %s #%d)", i.ConflictDate, i.Generation))
}

type implicitTeam struct {
	TeamID      keybase1.TeamID        `json:"team_id"`
	DisplayName string                 `json:"display_name"`
	Private     bool                   `json:"is_private"`
	Conflicts   []implicitTeamConflict `json:"conflicts,omitempty"`
	Status      libkb.AppStatus        `json:"status"`
}

func (i *implicitTeam) GetAppStatus() *libkb.AppStatus {
	return &i.Status
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupImplicitTeam(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, err error) {

	teamID, teamName, impTeamName, tlfID, _, err = LookupImplicitTeamAndConflicts(ctx, g, displayName, public)
	return teamID, teamName, impTeamName, tlfID, err
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupImplicitTeamAndConflicts(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, conflicts []keybase1.ImplicitTeamConflictInfo, err error) {
	impName, err := ResolveImplicitTeamDisplayName(ctx, g, displayName, public)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	return lookupImplicitTeamAndConflicts(ctx, g, displayName, impName)
}

// Lookup an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Does not resolve social assertions.
// preResolveDisplayName is used for logging and errors
func lookupImplicitTeamAndConflicts(ctx context.Context, g *libkb.GlobalContext,
	preResolveDisplayName string, impTeamNameInput keybase1.ImplicitTeamDisplayName) (
	teamID keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, conflicts []keybase1.ImplicitTeamConflictInfo, err error) {

	defer g.CTraceTimed(ctx, fmt.Sprintf("lookupImplicitTeamAndConflicts(%v)", preResolveDisplayName), func() error { return err })()

	impTeamName = impTeamNameInput

	// Use a copy without the conflict info to hit the api endpoint
	var impTeamNameWithoutConflict keybase1.ImplicitTeamDisplayName
	impTeamNameWithoutConflict = impTeamName
	impTeamNameWithoutConflict.ConflictInfo = nil
	lookupNameWithoutConflict, err := FormatImplicitTeamDisplayName(ctx, g, impTeamNameWithoutConflict)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}

	arg := libkb.NewAPIArgWithNetContext(ctx, "team/implicit")
	arg.SessionType = libkb.APISessionTypeOPTIONAL
	arg.Args = libkb.HTTPArgs{
		"display_name": libkb.S{Val: lookupNameWithoutConflict},
		"public":       libkb.B{Val: impTeamName.IsPublic},
	}
	var imp implicitTeam
	if err = g.API.GetDecode(arg, &imp); err != nil {
		if aerr, ok := err.(libkb.AppStatusError); ok {
			code := keybase1.StatusCode(aerr.Code)
			switch code {
			case keybase1.StatusCode_SCTeamReadError:
				return teamID, teamName, impTeamName, tlfID, conflicts, NewTeamDoesNotExistError(
					impTeamName.IsPublic, preResolveDisplayName)
			case keybase1.StatusCode_SCTeamProvisionalCanKey, keybase1.StatusCode_SCTeamProvisionalCannotKey:
				return teamID, teamName, impTeamName, tlfID, conflicts, libkb.NewTeamProvisionalError(
					(code == keybase1.StatusCode_SCTeamProvisionalCanKey), impTeamName.IsPublic, preResolveDisplayName)
			}
		}
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	if len(imp.Conflicts) > 0 {
		g.Log.CDebugf(ctx, "LookupImplicitTeam found %v conflicts", len(imp.Conflicts))
	}
	// We will use this team. Changed later if we selected a conflict.
	var foundSelectedConflict bool
	teamID = imp.TeamID
	for i, conflict := range imp.Conflicts {
		g.Log.CDebugf(ctx, "| checking conflict: %+v (iter %d)", conflict, i)
		conflictInfo, err := conflict.parse()

		if err != nil {
			// warn, don't fail
			g.Log.CWarningf(ctx, "LookupImplicitTeam got conflict suffix: %v", err)
			err = nil
			continue
		}
		conflicts = append(conflicts, *conflictInfo)

		if conflictInfo == nil {
			g.Log.CDebugf(ctx, "| got unexpected nil conflictInfo (iter %d)", i)
			continue
		}

		g.Log.CDebugf(ctx, "| parsed conflict into conflictInfo: %+v", *conflictInfo)

		if impTeamName.ConflictInfo != nil {
			match := libkb.FormatImplicitTeamDisplayNameSuffix(*impTeamName.ConflictInfo) == libkb.FormatImplicitTeamDisplayNameSuffix(*conflictInfo)
			if match {
				teamID = conflict.TeamID
				foundSelectedConflict = true
				g.Log.CDebugf(ctx, "| found conflict suffix match: %v", teamID)
			} else {
				g.Log.CDebugf(ctx, "| conflict suffix didn't match (teamID %v)", conflict.TeamID)
			}
		}
	}
	if impTeamName.ConflictInfo != nil && !foundSelectedConflict {
		// We got the team but didn't find the specific conflict requested.
		return teamID, teamName, impTeamName, tlfID, conflicts, NewTeamDoesNotExistError(
			impTeamName.IsPublic, "could not find team with suffix: %v", preResolveDisplayName)
	}
	team, err := Load(ctx, g, keybase1.LoadTeamArg{
		ID:          teamID,
		Public:      impTeamName.IsPublic,
		ForceRepoll: true,
	})
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}

	// Check the display names. This is how we make sure the server returned a team with the right members.
	teamDisplayName, err := team.ImplicitTeamDisplayNameString(ctx)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	referenceImpName, err := FormatImplicitTeamDisplayName(ctx, g, impTeamName)
	if err != nil {
		return teamID, teamName, impTeamName, tlfID, conflicts, err
	}
	if teamDisplayName != referenceImpName {
		return teamID, teamName, impTeamName, tlfID, conflicts, fmt.Errorf("implicit team name mismatch: %s != %s",
			teamDisplayName, referenceImpName)
	}
	if team.IsPublic() != impTeamName.IsPublic {
		return teamID, teamName, impTeamName, tlfID, conflicts, fmt.Errorf("implicit team public-ness mismatch: %v != %v", team.IsPublic(), impTeamName.IsPublic)
	}

	tlfID = team.KBFSTLFID()

	return teamID, team.Name(), impTeamName, tlfID, conflicts, nil
}

// Lookup or create an implicit team by name like "alice,bob+bob@twitter (conflicted copy 2017-03-04 #1)"
// Resolves social assertions.
func LookupOrCreateImplicitTeam(ctx context.Context, g *libkb.GlobalContext, displayName string, public bool) (res keybase1.TeamID, teamName keybase1.TeamName, impTeamName keybase1.ImplicitTeamDisplayName, tlfID keybase1.TLFID, err error) {
	defer g.CTraceTimed(ctx, fmt.Sprintf("LookupOrCreateImplicitTeam(%v)", displayName),
		func() error { return err })()
	lookupName, err := ResolveImplicitTeamDisplayName(ctx, g, displayName, public)
{'fraction_non_alphanumeric': 0.10274944268053773, 'fraction_numerical': 0.009389988515841384, 'mean_word_length': 4.995949777237748, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6498175', 'n_tokens_mistral': 5143, 'n_tokens_neox': 4782, 'n_words': 1223}