code.gitea.io/gitea@v1.21.7/services/convert/convert.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // Copyright 2018 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package convert
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	asymkey_model "code.gitea.io/gitea/models/asymkey"
    15  	"code.gitea.io/gitea/models/auth"
    16  	git_model "code.gitea.io/gitea/models/git"
    17  	issues_model "code.gitea.io/gitea/models/issues"
    18  	"code.gitea.io/gitea/models/organization"
    19  	"code.gitea.io/gitea/models/perm"
    20  	access_model "code.gitea.io/gitea/models/perm/access"
    21  	repo_model "code.gitea.io/gitea/models/repo"
    22  	"code.gitea.io/gitea/models/unit"
    23  	user_model "code.gitea.io/gitea/models/user"
    24  	"code.gitea.io/gitea/modules/git"
    25  	"code.gitea.io/gitea/modules/log"
    26  	api "code.gitea.io/gitea/modules/structs"
    27  	"code.gitea.io/gitea/modules/util"
    28  	"code.gitea.io/gitea/services/gitdiff"
    29  )
    30  
    31  // ToEmail convert models.EmailAddress to api.Email
    32  func ToEmail(email *user_model.EmailAddress) *api.Email {
    33  	return &api.Email{
    34  		Email:    email.Email,
    35  		Verified: email.IsActivated,
    36  		Primary:  email.IsPrimary,
    37  	}
    38  }
    39  
    40  // ToEmail convert models.EmailAddress to api.Email
    41  func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email {
    42  	return &api.Email{
    43  		Email:    email.Email,
    44  		Verified: email.IsActivated,
    45  		Primary:  email.IsPrimary,
    46  		UserID:   email.UID,
    47  		UserName: email.Name,
    48  	}
    49  }
    50  
    51  // ToBranch convert a git.Commit and git.Branch to an api.Branch
    52  func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName string, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
    53  	if bp == nil {
    54  		var hasPerm bool
    55  		var canPush bool
    56  		var err error
    57  		if user != nil {
    58  			hasPerm, err = access_model.HasAccessUnit(ctx, user, repo, unit.TypeCode, perm.AccessModeWrite)
    59  			if err != nil {
    60  				return nil, err
    61  			}
    62  
    63  			perms, err := access_model.GetUserRepoPermission(ctx, repo, user)
    64  			if err != nil {
    65  				return nil, err
    66  			}
    67  			canPush = issues_model.CanMaintainerWriteToBranch(ctx, perms, branchName, user)
    68  		}
    69  
    70  		return &api.Branch{
    71  			Name:                branchName,
    72  			Commit:              ToPayloadCommit(ctx, repo, c),
    73  			Protected:           false,
    74  			RequiredApprovals:   0,
    75  			EnableStatusCheck:   false,
    76  			StatusCheckContexts: []string{},
    77  			UserCanPush:         canPush,
    78  			UserCanMerge:        hasPerm,
    79  		}, nil
    80  	}
    81  
    82  	branch := &api.Branch{
    83  		Name:                branchName,
    84  		Commit:              ToPayloadCommit(ctx, repo, c),
    85  		Protected:           true,
    86  		RequiredApprovals:   bp.RequiredApprovals,
    87  		EnableStatusCheck:   bp.EnableStatusCheck,
    88  		StatusCheckContexts: bp.StatusCheckContexts,
    89  	}
    90  
    91  	if isRepoAdmin {
    92  		branch.EffectiveBranchProtectionName = bp.RuleName
    93  	}
    94  
    95  	if user != nil {
    96  		permission, err := access_model.GetUserRepoPermission(ctx, repo, user)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		bp.Repo = repo
   101  		branch.UserCanPush = bp.CanUserPush(ctx, user)
   102  		branch.UserCanMerge = git_model.IsUserMergeWhitelisted(ctx, bp, user.ID, permission)
   103  	}
   104  
   105  	return branch, nil
   106  }
   107  
   108  // ToBranchProtection convert a ProtectedBranch to api.BranchProtection
   109  func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api.BranchProtection {
   110  	pushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.WhitelistUserIDs)
   111  	if err != nil {
   112  		log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
   113  	}
   114  	mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs)
   115  	if err != nil {
   116  		log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
   117  	}
   118  	approvalsWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ApprovalsWhitelistUserIDs)
   119  	if err != nil {
   120  		log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
   121  	}
   122  	pushWhitelistTeams, err := organization.GetTeamNamesByID(bp.WhitelistTeamIDs)
   123  	if err != nil {
   124  		log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
   125  	}
   126  	mergeWhitelistTeams, err := organization.GetTeamNamesByID(bp.MergeWhitelistTeamIDs)
   127  	if err != nil {
   128  		log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
   129  	}
   130  	approvalsWhitelistTeams, err := organization.GetTeamNamesByID(bp.ApprovalsWhitelistTeamIDs)
   131  	if err != nil {
   132  		log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
   133  	}
   134  
   135  	branchName := ""
   136  	if !git_model.IsRuleNameSpecial(bp.RuleName) {
   137  		branchName = bp.RuleName
   138  	}
   139  
   140  	return &api.BranchProtection{
   141  		BranchName:                    branchName,
   142  		RuleName:                      bp.RuleName,
   143  		EnablePush:                    bp.CanPush,
   144  		EnablePushWhitelist:           bp.EnableWhitelist,
   145  		PushWhitelistUsernames:        pushWhitelistUsernames,
   146  		PushWhitelistTeams:            pushWhitelistTeams,
   147  		PushWhitelistDeployKeys:       bp.WhitelistDeployKeys,
   148  		EnableMergeWhitelist:          bp.EnableMergeWhitelist,
   149  		MergeWhitelistUsernames:       mergeWhitelistUsernames,
   150  		MergeWhitelistTeams:           mergeWhitelistTeams,
   151  		EnableStatusCheck:             bp.EnableStatusCheck,
   152  		StatusCheckContexts:           bp.StatusCheckContexts,
   153  		RequiredApprovals:             bp.RequiredApprovals,
   154  		EnableApprovalsWhitelist:      bp.EnableApprovalsWhitelist,
   155  		ApprovalsWhitelistUsernames:   approvalsWhitelistUsernames,
   156  		ApprovalsWhitelistTeams:       approvalsWhitelistTeams,
   157  		BlockOnRejectedReviews:        bp.BlockOnRejectedReviews,
   158  		BlockOnOfficialReviewRequests: bp.BlockOnOfficialReviewRequests,
   159  		BlockOnOutdatedBranch:         bp.BlockOnOutdatedBranch,
   160  		DismissStaleApprovals:         bp.DismissStaleApprovals,
   161  		RequireSignedCommits:          bp.RequireSignedCommits,
   162  		ProtectedFilePatterns:         bp.ProtectedFilePatterns,
   163  		UnprotectedFilePatterns:       bp.UnprotectedFilePatterns,
   164  		Created:                       bp.CreatedUnix.AsTime(),
   165  		Updated:                       bp.UpdatedUnix.AsTime(),
   166  	}
   167  }
   168  
   169  // ToTag convert a git.Tag to an api.Tag
   170  func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag {
   171  	return &api.Tag{
   172  		Name:       t.Name,
   173  		Message:    strings.TrimSpace(t.Message),
   174  		ID:         t.ID.String(),
   175  		Commit:     ToCommitMeta(repo, t),
   176  		ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
   177  		TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
   178  	}
   179  }
   180  
   181  // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
   182  func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification {
   183  	verif := asymkey_model.ParseCommitWithSignature(ctx, c)
   184  	commitVerification := &api.PayloadCommitVerification{
   185  		Verified: verif.Verified,
   186  		Reason:   verif.Reason,
   187  	}
   188  	if c.Signature != nil {
   189  		commitVerification.Signature = c.Signature.Signature
   190  		commitVerification.Payload = c.Signature.Payload
   191  	}
   192  	if verif.SigningUser != nil {
   193  		commitVerification.Signer = &api.PayloadUser{
   194  			Name:  verif.SigningUser.Name,
   195  			Email: verif.SigningUser.Email,
   196  		}
   197  	}
   198  	return commitVerification
   199  }
   200  
   201  // ToPublicKey convert asymkey_model.PublicKey to api.PublicKey
   202  func ToPublicKey(apiLink string, key *asymkey_model.PublicKey) *api.PublicKey {
   203  	return &api.PublicKey{
   204  		ID:          key.ID,
   205  		Key:         key.Content,
   206  		URL:         fmt.Sprintf("%s%d", apiLink, key.ID),
   207  		Title:       key.Name,
   208  		Fingerprint: key.Fingerprint,
   209  		Created:     key.CreatedUnix.AsTime(),
   210  	}
   211  }
   212  
   213  // ToGPGKey converts models.GPGKey to api.GPGKey
   214  func ToGPGKey(key *asymkey_model.GPGKey) *api.GPGKey {
   215  	subkeys := make([]*api.GPGKey, len(key.SubsKey))
   216  	for id, k := range key.SubsKey {
   217  		subkeys[id] = &api.GPGKey{
   218  			ID:                k.ID,
   219  			PrimaryKeyID:      k.PrimaryKeyID,
   220  			KeyID:             k.KeyID,
   221  			PublicKey:         k.Content,
   222  			Created:           k.CreatedUnix.AsTime(),
   223  			Expires:           k.ExpiredUnix.AsTime(),
   224  			CanSign:           k.CanSign,
   225  			CanEncryptComms:   k.CanEncryptComms,
   226  			CanEncryptStorage: k.CanEncryptStorage,
   227  			CanCertify:        k.CanSign,
   228  			Verified:          k.Verified,
   229  		}
   230  	}
   231  	emails := make([]*api.GPGKeyEmail, len(key.Emails))
   232  	for i, e := range key.Emails {
   233  		emails[i] = ToGPGKeyEmail(e)
   234  	}
   235  	return &api.GPGKey{
   236  		ID:                key.ID,
   237  		PrimaryKeyID:      key.PrimaryKeyID,
   238  		KeyID:             key.KeyID,
   239  		PublicKey:         key.Content,
   240  		Created:           key.CreatedUnix.AsTime(),
   241  		Expires:           key.ExpiredUnix.AsTime(),
   242  		Emails:            emails,
   243  		SubsKey:           subkeys,
   244  		CanSign:           key.CanSign,
   245  		CanEncryptComms:   key.CanEncryptComms,
   246  		CanEncryptStorage: key.CanEncryptStorage,
   247  		CanCertify:        key.CanSign,
   248  		Verified:          key.Verified,
   249  	}
   250  }
   251  
   252  // ToGPGKeyEmail convert models.EmailAddress to api.GPGKeyEmail
   253  func ToGPGKeyEmail(email *user_model.EmailAddress) *api.GPGKeyEmail {
   254  	return &api.GPGKeyEmail{
   255  		Email:    email.Email,
   256  		Verified: email.IsActivated,
   257  	}
   258  }
   259  
   260  // ToGitHook convert git.Hook to api.GitHook
   261  func ToGitHook(h *git.Hook) *api.GitHook {
   262  	return &api.GitHook{
   263  		Name:     h.Name(),
   264  		IsActive: h.IsActive,
   265  		Content:  h.Content,
   266  	}
   267  }
   268  
   269  // ToDeployKey convert asymkey_model.DeployKey to api.DeployKey
   270  func ToDeployKey(apiLink string, key *asymkey_model.DeployKey) *api.DeployKey {
   271  	return &api.DeployKey{
   272  		ID:          key.ID,
   273  		KeyID:       key.KeyID,
   274  		Key:         key.Content,
   275  		Fingerprint: key.Fingerprint,
   276  		URL:         fmt.Sprintf("%s%d", apiLink, key.ID),
   277  		Title:       key.Name,
   278  		Created:     key.CreatedUnix.AsTime(),
   279  		ReadOnly:    key.Mode == perm.AccessModeRead, // All deploy keys are read-only.
   280  	}
   281  }
   282  
   283  // ToOrganization convert user_model.User to api.Organization
   284  func ToOrganization(ctx context.Context, org *organization.Organization) *api.Organization {
   285  	return &api.Organization{
   286  		ID:                        org.ID,
   287  		AvatarURL:                 org.AsUser().AvatarLink(ctx),
   288  		Name:                      org.Name,
   289  		UserName:                  org.Name,
   290  		FullName:                  org.FullName,
   291  		Email:                     org.Email,
   292  		Description:               org.Description,
   293  		Website:                   org.Website,
   294  		Location:                  org.Location,
   295  		Visibility:                org.Visibility.String(),
   296  		RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess,
   297  	}
   298  }
   299  
   300  // ToTeam convert models.Team to api.Team
   301  func ToTeam(ctx context.Context, team *organization.Team, loadOrg ...bool) (*api.Team, error) {
   302  	teams, err := ToTeams(ctx, []*organization.Team{team}, len(loadOrg) != 0 && loadOrg[0])
   303  	if err != nil || len(teams) == 0 {
   304  		return nil, err
   305  	}
   306  	return teams[0], nil
   307  }
   308  
   309  // ToTeams convert models.Team list to api.Team list
   310  func ToTeams(ctx context.Context, teams []*organization.Team, loadOrgs bool) ([]*api.Team, error) {
   311  	cache := make(map[int64]*api.Organization)
   312  	apiTeams := make([]*api.Team, 0, len(teams))
   313  	for _, t := range teams {
   314  		if err := t.LoadUnits(ctx); err != nil {
   315  			return nil, err
   316  		}
   317  
   318  		apiTeam := &api.Team{
   319  			ID:                      t.ID,
   320  			Name:                    t.Name,
   321  			Description:             t.Description,
   322  			IncludesAllRepositories: t.IncludesAllRepositories,
   323  			CanCreateOrgRepo:        t.CanCreateOrgRepo,
   324  			Permission:              t.AccessMode.String(),
   325  			Units:                   t.GetUnitNames(),
   326  			UnitsMap:                t.GetUnitsMap(),
   327  		}
   328  
   329  		if loadOrgs {
   330  			apiOrg, ok := cache[t.OrgID]
   331  			if !ok {
   332  				org, err := organization.GetOrgByID(ctx, t.OrgID)
   333  				if err != nil {
   334  					return nil, err
   335  				}
   336  				apiOrg = ToOrganization(ctx, org)
   337  				cache[t.OrgID] = apiOrg
   338  			}
   339  			apiTeam.Organization = apiOrg
   340  		}
   341  
   342  		apiTeams = append(apiTeams, apiTeam)
   343  	}
   344  	return apiTeams, nil
   345  }
   346  
   347  // ToAnnotatedTag convert git.Tag to api.AnnotatedTag
   348  func ToAnnotatedTag(ctx context.Context, repo *repo_model.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag {
   349  	return &api.AnnotatedTag{
   350  		Tag:          t.Name,
   351  		SHA:          t.ID.String(),
   352  		Object:       ToAnnotatedTagObject(repo, c),
   353  		Message:      t.Message,
   354  		URL:          util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
   355  		Tagger:       ToCommitUser(t.Tagger),
   356  		Verification: ToVerification(ctx, c),
   357  	}
   358  }
   359  
   360  // ToAnnotatedTagObject convert a git.Commit to an api.AnnotatedTagObject
   361  func ToAnnotatedTagObject(repo *repo_model.Repository, commit *git.Commit) *api.AnnotatedTagObject {
   362  	return &api.AnnotatedTagObject{
   363  		SHA:  commit.ID.String(),
   364  		Type: string(git.ObjectCommit),
   365  		URL:  util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
   366  	}
   367  }
   368  
   369  // ToTopicResponse convert from models.Topic to api.TopicResponse
   370  func ToTopicResponse(topic *repo_model.Topic) *api.TopicResponse {
   371  	return &api.TopicResponse{
   372  		ID:        topic.ID,
   373  		Name:      topic.Name,
   374  		RepoCount: topic.RepoCount,
   375  		Created:   topic.CreatedUnix.AsTime(),
   376  		Updated:   topic.UpdatedUnix.AsTime(),
   377  	}
   378  }
   379  
   380  // ToOAuth2Application convert from auth.OAuth2Application to api.OAuth2Application
   381  func ToOAuth2Application(app *auth.OAuth2Application) *api.OAuth2Application {
   382  	return &api.OAuth2Application{
   383  		ID:                 app.ID,
   384  		Name:               app.Name,
   385  		ClientID:           app.ClientID,
   386  		ClientSecret:       app.ClientSecret,
   387  		ConfidentialClient: app.ConfidentialClient,
   388  		RedirectURIs:       app.RedirectURIs,
   389  		Created:            app.CreatedUnix.AsTime(),
   390  	}
   391  }
   392  
   393  // ToLFSLock convert a LFSLock to api.LFSLock
   394  func ToLFSLock(ctx context.Context, l *git_model.LFSLock) *api.LFSLock {
   395  	u, err := user_model.GetUserByID(ctx, l.OwnerID)
   396  	if err != nil {
   397  		return nil
   398  	}
   399  	return &api.LFSLock{
   400  		ID:       strconv.FormatInt(l.ID, 10),
   401  		Path:     l.Path,
   402  		LockedAt: l.Created.Round(time.Second),
   403  		Owner: &api.LFSLockOwner{
   404  			Name: u.Name,
   405  		},
   406  	}
   407  }
   408  
   409  // ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
   410  func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
   411  	status := "changed"
   412  	if f.IsDeleted {
   413  		status = "deleted"
   414  	} else if f.IsCreated {
   415  		status = "added"
   416  	} else if f.IsRenamed && f.Type == gitdiff.DiffFileCopy {
   417  		status = "copied"
   418  	} else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
   419  		status = "renamed"
   420  	} else if f.Addition == 0 && f.Deletion == 0 {
   421  		status = "unchanged"
   422  	}
   423  
   424  	file := &api.ChangedFile{
   425  		Filename:    f.GetDiffFileName(),
   426  		Status:      status,
   427  		Additions:   f.Addition,
   428  		Deletions:   f.Deletion,
   429  		Changes:     f.Addition + f.Deletion,
   430  		HTMLURL:     fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
   431  		ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
   432  		RawURL:      fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
   433  	}
   434  
   435  	if status == "rename" {
   436  		file.PreviousFilename = f.OldName
   437  	}
   438  
   439  	return file
   440  }