mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-08 14:34:49 +09:00
Add a build-time conversion step that transforms the existing Swagger 2.0 spec into an OpenAPI 3.0 spec. The OAS3 spec is served alongside the existing Swagger 2.0 spec, enabling API clients that require OAS3 to generate code directly from Gitea's API. This is not to be an answer to how gitea handles OAS3 long term, but a way to use what we have to move a step forward. --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
269 lines
11 KiB
Go
269 lines
11 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package convert
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
git_model "code.gitea.io/gitea/models/git"
|
|
"code.gitea.io/gitea/models/perm"
|
|
access_model "code.gitea.io/gitea/models/perm/access"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
unit_model "code.gitea.io/gitea/models/unit"
|
|
"code.gitea.io/gitea/modules/log"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/util"
|
|
)
|
|
|
|
// ToRepo converts a Repository to api.Repository
|
|
func ToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission) *api.Repository {
|
|
return innerToRepo(ctx, repo, permissionInRepo, false)
|
|
}
|
|
|
|
func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInRepo access_model.Permission, isParent bool) *api.Repository {
|
|
var parent *api.Repository
|
|
|
|
if !permissionInRepo.HasUnits() && permissionInRepo.AccessMode > perm.AccessModeNone {
|
|
// If units is empty, it means that it's a hard-coded permission, like access_model.Permission{AccessMode: perm.AccessModeAdmin}
|
|
// So we need to load units for the repo, otherwise UnitAccessMode will just return perm.AccessModeNone.
|
|
// TODO: this logic is still not right (because unit modes are not correctly prepared)
|
|
// the caller should prepare a proper "permission" before calling this function.
|
|
_ = repo.LoadUnits(ctx) // the error is not important, so ignore it
|
|
permissionInRepo.SetUnitsWithDefaultAccessMode(repo.Units, permissionInRepo.AccessMode)
|
|
}
|
|
|
|
// TODO: ideally we should pass "doer" into "ToRepo" to make CloneLink could generate user-related links
|
|
// And passing "doer" in will also fix other FIXMEs in this file.
|
|
cloneLink := repo.CloneLinkGeneral(ctx) // no doer at the moment
|
|
permission := &api.Permission{
|
|
Admin: permissionInRepo.AccessMode >= perm.AccessModeAdmin,
|
|
Push: permissionInRepo.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeWrite,
|
|
Pull: permissionInRepo.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead,
|
|
}
|
|
if !isParent {
|
|
err := repo.GetBaseRepo(ctx)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if repo.BaseRepo != nil {
|
|
// FIXME: The permission of the parent repo is not correct.
|
|
// It's the permission of the current repo, so it's probably different from the parent repo.
|
|
// But there isn't a good way to get the permission of the parent repo, because the doer is not passed in.
|
|
// Use the permission of the current repo to keep the behavior consistent with the old API.
|
|
// Maybe the right way is setting the permission of the parent repo to nil, empty is better than wrong.
|
|
parent = innerToRepo(ctx, repo.BaseRepo, permissionInRepo, true)
|
|
}
|
|
}
|
|
|
|
// check enabled/disabled units
|
|
hasIssues := false
|
|
var externalTracker *api.ExternalTracker
|
|
var internalTracker *api.InternalTracker
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypeIssues); err == nil {
|
|
config := unit.IssuesConfig()
|
|
hasIssues = true
|
|
internalTracker = &api.InternalTracker{
|
|
EnableTimeTracker: config.EnableTimetracker,
|
|
AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime,
|
|
EnableIssueDependencies: config.EnableDependencies,
|
|
}
|
|
} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil {
|
|
config := unit.ExternalTrackerConfig()
|
|
hasIssues = true
|
|
externalTracker = &api.ExternalTracker{
|
|
ExternalTrackerURL: config.ExternalTrackerURL,
|
|
ExternalTrackerFormat: config.ExternalTrackerFormat,
|
|
ExternalTrackerStyle: config.ExternalTrackerStyle,
|
|
ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern,
|
|
}
|
|
}
|
|
hasWiki := false
|
|
var externalWiki *api.ExternalWiki
|
|
if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil {
|
|
hasWiki = true
|
|
} else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil {
|
|
hasWiki = true
|
|
config := unit.ExternalWikiConfig()
|
|
externalWiki = &api.ExternalWiki{
|
|
ExternalWikiURL: config.ExternalWikiURL,
|
|
}
|
|
}
|
|
hasPullRequests := false
|
|
ignoreWhitespaceConflicts := false
|
|
allowMerge := false
|
|
allowRebase := false
|
|
allowRebaseMerge := false
|
|
allowSquash := false
|
|
allowFastForwardOnly := false
|
|
allowRebaseUpdate := false
|
|
allowManualMerge := true
|
|
autodetectManualMerge := false
|
|
defaultDeleteBranchAfterMerge := false
|
|
defaultMergeStyle := repo_model.MergeStyleMerge
|
|
defaultAllowMaintainerEdit := false
|
|
defaultTargetBranch := ""
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil {
|
|
config := unit.PullRequestsConfig()
|
|
hasPullRequests = true
|
|
ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts
|
|
allowMerge = config.AllowMerge
|
|
allowRebase = config.AllowRebase
|
|
allowRebaseMerge = config.AllowRebaseMerge
|
|
allowSquash = config.AllowSquash
|
|
allowFastForwardOnly = config.AllowFastForwardOnly
|
|
allowRebaseUpdate = config.AllowRebaseUpdate
|
|
allowManualMerge = config.AllowManualMerge
|
|
autodetectManualMerge = config.AutodetectManualMerge
|
|
defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge
|
|
defaultMergeStyle = config.DefaultMergeStyle
|
|
defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit
|
|
defaultTargetBranch = config.DefaultTargetBranch
|
|
}
|
|
hasProjects := false
|
|
projectsMode := repo_model.ProjectsModeAll
|
|
if unit, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil {
|
|
hasProjects = true
|
|
config := unit.ProjectsConfig()
|
|
projectsMode = config.ProjectsMode
|
|
}
|
|
|
|
hasCode := repo.UnitEnabled(ctx, unit_model.TypeCode)
|
|
hasReleases := repo.UnitEnabled(ctx, unit_model.TypeReleases)
|
|
hasPackages := repo.UnitEnabled(ctx, unit_model.TypePackages)
|
|
hasActions := repo.UnitEnabled(ctx, unit_model.TypeActions)
|
|
|
|
if err := repo.LoadOwner(ctx); err != nil {
|
|
return nil
|
|
}
|
|
|
|
numReleases, _ := db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
|
IncludeDrafts: false,
|
|
IncludeTags: false,
|
|
RepoID: repo.ID,
|
|
})
|
|
|
|
branchCount, err := git_model.CountBranches(ctx, repo.ID, false)
|
|
if err != nil {
|
|
log.Error("CountBranches [%d]: %v", repo.ID, err)
|
|
}
|
|
|
|
mirrorInterval := ""
|
|
var mirrorUpdated time.Time
|
|
if repo.IsMirror {
|
|
pullMirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
|
|
if err == nil {
|
|
mirrorInterval = pullMirror.Interval.String()
|
|
mirrorUpdated = pullMirror.UpdatedUnix.AsTime()
|
|
}
|
|
}
|
|
|
|
var transfer *api.RepoTransfer
|
|
if repo.Status == repo_model.RepositoryPendingTransfer {
|
|
t, err := repo_model.GetPendingRepositoryTransfer(ctx, repo)
|
|
if err != nil && !repo_model.IsErrNoPendingTransfer(err) {
|
|
log.Warn("GetPendingRepositoryTransfer: %v", err)
|
|
} else {
|
|
if err := t.LoadAttributes(ctx); err != nil {
|
|
log.Warn("LoadAttributes of RepoTransfer: %v", err)
|
|
} else {
|
|
transfer = ToRepoTransfer(ctx, t)
|
|
}
|
|
}
|
|
}
|
|
|
|
var language string
|
|
if repo.PrimaryLanguage != nil {
|
|
language = repo.PrimaryLanguage.Language
|
|
}
|
|
|
|
repoLicenses, err := repo_model.GetRepoLicenses(ctx, repo)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
repoAPIURL := repo.APIURL()
|
|
|
|
return &api.Repository{
|
|
ID: repo.ID,
|
|
Owner: ToUserWithAccessMode(ctx, repo.Owner, permissionInRepo.AccessMode),
|
|
Name: repo.Name,
|
|
FullName: repo.FullName(),
|
|
Description: repo.Description,
|
|
Private: repo.IsPrivate,
|
|
Template: repo.IsTemplate,
|
|
Empty: repo.IsEmpty,
|
|
Archived: repo.IsArchived,
|
|
Size: int(repo.Size / 1024),
|
|
Fork: repo.IsFork,
|
|
Parent: parent,
|
|
Mirror: repo.IsMirror,
|
|
HTMLURL: repo.HTMLURL(ctx),
|
|
URL: repoAPIURL,
|
|
SSHURL: cloneLink.SSH,
|
|
CloneURL: cloneLink.HTTPS,
|
|
OriginalURL: repo.SanitizedOriginalURL(),
|
|
Website: repo.Website,
|
|
Language: language,
|
|
LanguagesURL: repoAPIURL + "/languages",
|
|
Stars: repo.NumStars,
|
|
Forks: repo.NumForks,
|
|
Watchers: repo.NumWatches,
|
|
BranchCount: int(branchCount),
|
|
OpenIssues: repo.NumOpenIssues,
|
|
OpenPulls: repo.NumOpenPulls,
|
|
Releases: int(numReleases),
|
|
DefaultBranch: repo.DefaultBranch,
|
|
Created: repo.CreatedUnix.AsTime(),
|
|
Updated: repo.UpdatedUnix.AsTime(),
|
|
ArchivedAt: repo.ArchivedUnix.AsTime(),
|
|
Permissions: permission,
|
|
HasCode: hasCode,
|
|
HasIssues: hasIssues,
|
|
ExternalTracker: externalTracker,
|
|
InternalTracker: internalTracker,
|
|
HasWiki: hasWiki,
|
|
HasProjects: hasProjects,
|
|
ProjectsMode: string(projectsMode),
|
|
HasReleases: hasReleases,
|
|
HasPackages: hasPackages,
|
|
HasActions: hasActions,
|
|
ExternalWiki: externalWiki,
|
|
HasPullRequests: hasPullRequests,
|
|
IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts,
|
|
AllowMerge: allowMerge,
|
|
AllowRebase: allowRebase,
|
|
AllowRebaseMerge: allowRebaseMerge,
|
|
AllowSquash: allowSquash,
|
|
AllowFastForwardOnly: allowFastForwardOnly,
|
|
AllowRebaseUpdate: allowRebaseUpdate,
|
|
AllowManualMerge: allowManualMerge,
|
|
AutodetectManualMerge: autodetectManualMerge,
|
|
DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge,
|
|
DefaultMergeStyle: string(defaultMergeStyle),
|
|
DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit,
|
|
DefaultTargetBranch: defaultTargetBranch,
|
|
AvatarURL: repo.AvatarLink(ctx),
|
|
Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
|
|
MirrorInterval: mirrorInterval,
|
|
MirrorUpdated: mirrorUpdated,
|
|
RepoTransfer: transfer,
|
|
Topics: util.SliceNilAsEmpty(repo.Topics),
|
|
ObjectFormatName: api.ObjectFormatName(repo.ObjectFormatName),
|
|
Licenses: util.SliceNilAsEmpty(repoLicenses.StringList()),
|
|
}
|
|
}
|
|
|
|
// ToRepoTransfer convert a models.RepoTransfer to a structs.RepeTransfer
|
|
func ToRepoTransfer(ctx context.Context, t *repo_model.RepoTransfer) *api.RepoTransfer {
|
|
teams, _ := ToTeams(ctx, t.Teams, false)
|
|
|
|
return &api.RepoTransfer{
|
|
Doer: ToUser(ctx, t.Doer, nil),
|
|
Recipient: ToUser(ctx, t.Recipient, nil),
|
|
Teams: teams,
|
|
}
|
|
}
|