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 }