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

     1  // Copyright 2020 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package convert
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"code.gitea.io/gitea/models"
    11  	"code.gitea.io/gitea/models/db"
    12  	"code.gitea.io/gitea/models/perm"
    13  	access_model "code.gitea.io/gitea/models/perm/access"
    14  	repo_model "code.gitea.io/gitea/models/repo"
    15  	unit_model "code.gitea.io/gitea/models/unit"
    16  	"code.gitea.io/gitea/modules/log"
    17  	api "code.gitea.io/gitea/modules/structs"
    18  )
    19  
    20  // ToRepo converts a Repository to api.Repository
    21  func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission) *api.Repository {
    22  	return innerToRepo(ctx, repo, permissionInRepo, false)
    23  }
    24  
    25  func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
    26  	var parent *api.Repository
    27  
    28  	if !permissionInRepo.HasUnits() && permissionInRepo.AccessMode > perm.AccessModeNone {
    29  		// If units is empty, it means that it's a hard-coded permission, like access_model.Permission{AccessMode: perm.AccessModeAdmin}
    30  		// So we need to load units for the repo, otherwise UnitAccessMode will just return perm.AccessModeNone.
    31  		// TODO: this logic is still not right (because unit modes are not correctly prepared)
    32  		//   the caller should prepare a proper "permission" before calling this function.
    33  		_ = repo.LoadUnits(ctx) // the error is not important, so ignore it
    34  		permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
    35  	}
    36  
    37  	cloneLink := repo.CloneLink()
    38  	permission := &api.Permission{
    39  		Admin: permissionInRepo.AccessMode >= perm.AccessModeAdmin,
    40  		Push:  permissionInRepo.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeWrite,
    41  		Pull:  permissionInRepo.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead,
    42  	}
    43  	if !isParent {
    44  		err := repo.GetBaseRepo(ctx)
    45  		if err != nil {
    46  			return nil
    47  		}
    48  		if repo.BaseRepo != nil {
    49  			// FIXME: The permission of the parent repo is not correct.
    50  			//        It's the permission of the current repo, so it's probably different from the parent repo.
    51  			//        But there isn't a good way to get the permission of the parent repo, because the doer is not passed in.
    52  			//        Use the permission of the current repo to keep the behavior consistent with the old API.
    53  			//        Maybe the right way is setting the permission of the parent repo to nil, empty is better than wrong.
    54  			parent = innerToRepo(ctx, repo.BaseRepo, permissionInRepo, true)
    55  		}
    56  	}
    57  
    58  	// check enabled/disabled units
    59  	hasIssues := false
    60  	var externalTracker *api.ExternalTracker
    61  	var internalTracker *api.InternalTracker
    62  	if unit, err := repo.GetUnit(ctx, unit_model.TypeIssues); err == nil {
    63  		config := unit.IssuesConfig()
    64  		hasIssues = true
    65  		internalTracker = &api.InternalTracker{
    66  			EnableTimeTracker:                config.EnableTimetracker,
    67  			AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime,
    68  			EnableIssueDependencies:          config.EnableDependencies,
    69  		}
    70  	} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil {
    71  		config := unit.ExternalTrackerConfig()
    72  		hasIssues = true
    73  		externalTracker = &api.ExternalTracker{
    74  			ExternalTrackerURL:           config.ExternalTrackerURL,
    75  			ExternalTrackerFormat:        config.ExternalTrackerFormat,
    76  			ExternalTrackerStyle:         config.ExternalTrackerStyle,
    77  			ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern,
    78  		}
    79  	}
    80  	hasWiki := false
    81  	var externalWiki *api.ExternalWiki
    82  	if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil {
    83  		hasWiki = true
    84  	} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil {
    85  		hasWiki = true
    86  		config := unit.ExternalWikiConfig()
    87  		externalWiki = &api.ExternalWiki{
    88  			ExternalWikiURL: config.ExternalWikiURL,
    89  		}
    90  	}
    91  	hasPullRequests := false
    92  	ignoreWhitespaceConflicts := false
    93  	allowMerge := false
    94  	allowRebase := false
    95  	allowRebaseMerge := false
    96  	allowSquash := false
    97  	allowFastForwardOnly := false
    98  	allowRebaseUpdate := false
    99  	defaultDeleteBranchAfterMerge := false
   100  	defaultMergeStyle := repo_model.MergeStyleMerge
   101  	defaultAllowMaintainerEdit := false
   102  	if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil {
   103  		config := unit.PullRequestsConfig()
   104  		hasPullRequests = true
   105  		ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
   106  		allowMerge = config.AllowMerge
   107  		allowRebase = config.AllowRebase
   108  		allowRebaseMerge = config.AllowRebaseMerge
   109  		allowSquash = config.AllowSquash
   110  		allowFastForwardOnly = config.AllowFastForwardOnly
   111  		allowRebaseUpdate = config.AllowRebaseUpdate
   112  		defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
   113  		defaultMergeStyle = config.GetDefaultMergeStyle()
   114  		defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
   115  	}
   116  	hasProjects := false
   117  	projectsMode := repo_model.ProjectsModeAll
   118  	if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil {
   119  		hasProjects = true
   120  		config := unit.ProjectsConfig()
   121  		projectsMode = config.ProjectsMode
   122  	}
   123  
   124  	hasReleases := false
   125  	if _, err := repo.GetUnit(ctx, unit_model.TypeReleases); err == nil {
   126  		hasReleases = true
   127  	}
   128  
   129  	hasPackages := false
   130  	if _, err := repo.GetUnit(ctx, unit_model.TypePackages); err == nil {
   131  		hasPackages = true
   132  	}
   133  
   134  	hasActions := false
   135  	if _, err := repo.GetUnit(ctx, unit_model.TypeActions); err == nil {
   136  		hasActions = true
   137  	}
   138  
   139  	if err := repo.LoadOwner(ctx); err != nil {
   140  		return nil
   141  	}
   142  
   143  	numReleases, _ := db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
   144  		IncludeDrafts: false,
   145  		IncludeTags:   false,
   146  		RepoID:        repo.ID,
   147  	})
   148  
   149  	mirrorInterval := ""
   150  	var mirrorUpdated time.Time
   151  	if repo.IsMirror {
   152  		pullMirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
   153  		if err == nil {
   154  			mirrorInterval = pullMirror.Interval.String()
   155  			mirrorUpdated = pullMirror.UpdatedUnix.AsTime()
   156  		}
   157  	}
   158  
   159  	var transfer *api.RepoTransfer
   160  	if repo.Status == repo_model.RepositoryPendingTransfer {
   161  		t, err := models.GetPendingRepositoryTransfer(ctx, repo)
   162  		if err != nil && !models.IsErrNoPendingTransfer(err) {
   163  			log.Warn("GetPendingRepositoryTransfer: %v", err)
   164  		} else {
   165  			if err := t.LoadAttributes(ctx); err != nil {
   166  				log.Warn("LoadAttributes of RepoTransfer: %v", err)
   167  			} else {
   168  				transfer = ToRepoTransfer(ctx, t)
   169  			}
   170  		}
   171  	}
   172  
   173  	var language string
   174  	if repo.PrimaryLanguage != nil {
   175  		language = repo.PrimaryLanguage.Language
   176  	}
   177  
   178  	repoAPIURL := repo.APIURL()
   179  
   180  	return &api.Repository{
   181  		ID:                            repo.ID,
   182  		Owner:                         ToUserWithAccessMode(ctx, repo.Owner, permissionInRepo.AccessMode),
   183  		Name:                          repo.Name,
   184  		FullName:                      repo.FullName(),
   185  		Description:                   repo.Description,
   186  		Private:                       repo.IsPrivate,
   187  		Template:                      repo.IsTemplate,
   188  		Empty:                         repo.IsEmpty,
   189  		Archived:                      repo.IsArchived,
   190  		Size:                          int(repo.Size / 1024),
   191  		Fork:                          repo.IsFork,
   192  		Parent:                        parent,
   193  		Mirror:                        repo.IsMirror,
   194  		HTMLURL:                       repo.HTMLURL(),
   195  		URL:                           repoAPIURL,
   196  		SSHURL:                        cloneLink.SSH,
   197  		CloneURL:                      cloneLink.HTTPS,
   198  		OriginalURL:                   repo.SanitizedOriginalURL(),
   199  		Website:                       repo.Website,
   200  		Language:                      language,
   201  		LanguagesURL:                  repoAPIURL + "/languages",
   202  		Stars:                         repo.NumStars,
   203  		Forks:                         repo.NumForks,
   204  		Watchers:                      repo.NumWatches,
   205  		OpenIssues:                    repo.NumOpenIssues,
   206  		OpenPulls:                     repo.NumOpenPulls,
   207  		Releases:                      int(numReleases),
   208  		DefaultBranch:                 repo.DefaultBranch,
   209  		Created:                       repo.CreatedUnix.AsTime(),
   210  		Updated:                       repo.UpdatedUnix.AsTime(),
   211  		ArchivedAt:                    repo.ArchivedUnix.AsTime(),
   212  		Permissions:                   permission,
   213  		HasIssues:                     hasIssues,
   214  		ExternalTracker:               externalTracker,
   215  		InternalTracker:               internalTracker,
   216  		HasWiki:                       hasWiki,
   217  		HasProjects:                   hasProjects,
   218  		ProjectsMode:                  string(projectsMode),
   219  		HasReleases:                   hasReleases,
   220  		HasPackages:                   hasPackages,
   221  		HasActions:                    hasActions,
   222  		ExternalWiki:                  externalWiki,
   223  		HasPullRequests:               hasPullRequests,
   224  		IgnoreWhitespaceConflicts:     ignoreWhitespaceConflicts,
   225  		AllowMerge:                    allowMerge,
   226  		AllowRebase:                   allowRebase,
   227  		AllowRebaseMerge:              allowRebaseMerge,
   228  		AllowSquash:                   allowSquash,
   229  		AllowFastForwardOnly:          allowFastForwardOnly,
   230  		AllowRebaseUpdate:             allowRebaseUpdate,
   231  		DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
   232  		DefaultMergeStyle:             string(defaultMergeStyle),
   233  		DefaultAllowMaintainerEdit:    defaultAllowMaintainerEdit,
   234  		AvatarURL:                     repo.AvatarLink(ctx),
   235  		Internal:                      !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
   236  		MirrorInterval:                mirrorInterval,
   237  		MirrorUpdated:                 mirrorUpdated,
   238  		RepoTransfer:                  transfer,
   239  		ObjectFormatName:              repo.ObjectFormatName,
   240  	}
   241  }
   242  
   243  // ToRepoTransfer convert a models.RepoTransfer to a structs.RepeTransfer
   244  func ToRepoTransfer(ctx context.Context, t *models.RepoTransfer) *api.RepoTransfer {
   245  	teams, _ := ToTeams(ctx, t.Teams, false)
   246  
   247  	return &api.RepoTransfer{
   248  		Doer:      ToUser(ctx, t.Doer, nil),
   249  		Recipient: ToUser(ctx, t.Recipient, nil),
   250  		Teams:     teams,
   251  	}
   252  }