mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-27 06:03:51 +03:00
feat(F3): CLI: f3 mirror to convert to/from Forgejo
feat(F3): driver stub feat(F3): util.Logger feat(F3): driver compliance tests feat(F3): driver/users implementation feat(F3): driver/user implementation feat(F3): driver/{projects,project} implementation feat(F3): driver/{labels,label} implementation feat(F3): driver/{milestones,milestone} implementation feat(F3): driver/{repositories,repository} implementation feat(F3): driver/{organizations,organization} implementation feat(F3): driver/{releases,release} implementation feat(F3): driver/{issues,issue} implementation feat(F3): driver/{comments,comment} implementation feat(F3): driver/{assets,asset} implementation feat(F3): driver/{pullrequests,pullrequest} implementation feat(F3): driver/{reviews,review} implementation feat(F3): driver/{topics,topic} implementation feat(F3): driver/{reactions,reaction} implementation feat(F3): driver/{reviewComments,reviewComment} implementation feat(F3): CLI: f3 mirror chore(F3): move to code.forgejo.org feat(f3): upgrade to gof3 3.1.0 repositories in pull requests are represented with a reference instead of an owner/project pair of names
This commit is contained in:
parent
64b67ba641
commit
e99d3f7055
56 changed files with 3991 additions and 5 deletions
29
assets/go-licenses.json
generated
29
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
|
@ -134,8 +134,8 @@ func validateSecret(secret string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
|
func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
|
||||||
if !ContextGetNoInit(ctx) {
|
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
|
if !ContextGetNoInit(ctx) {
|
||||||
ctx, cancel = installSignals(ctx)
|
ctx, cancel = installSignals(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
70
cmd/forgejo/f3.go
Normal file
70
cmd/forgejo/f3.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package forgejo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/services/f3/util"
|
||||||
|
|
||||||
|
_ "code.gitea.io/gitea/services/f3/driver" // register the driver
|
||||||
|
|
||||||
|
f3_cmd "code.forgejo.org/f3/gof3/v3/cmd"
|
||||||
|
f3_logger "code.forgejo.org/f3/gof3/v3/logger"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CmdF3(ctx context.Context) *cli.Command {
|
||||||
|
ctx = f3_logger.ContextSetLogger(ctx, util.NewF3Logger(nil, log.GetLogger(log.DEFAULT)))
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "f3",
|
||||||
|
Usage: "F3",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
SubcmdF3Mirror(ctx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubcmdF3Mirror(ctx context.Context) *cli.Command {
|
||||||
|
mirrorCmd := f3_cmd.CreateCmdMirror(ctx)
|
||||||
|
mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx)
|
||||||
|
f3Action := mirrorCmd.Action
|
||||||
|
mirrorCmd.Action = func(c *cli.Context) error { return runMirror(ctx, c, f3Action) }
|
||||||
|
return mirrorCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMirror(ctx context.Context, c *cli.Context, action cli.ActionFunc) error {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
if !ContextGetNoInit(ctx) {
|
||||||
|
ctx, cancel = installSignals(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := storage.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := git.InitSimple(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := models.Init(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := action(c)
|
||||||
|
if panicError, ok := err.(f3_util.PanicError); ok {
|
||||||
|
log.Debug("F3 Stack trace\n%s", panicError.Stack())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ func CmdForgejo(ctx context.Context) *cli.Command {
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{},
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
CmdActions(ctx),
|
CmdActions(ctx),
|
||||||
|
CmdF3(ctx),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
cmd/main.go
15
cmd/main.go
|
@ -124,6 +124,7 @@ func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
|
|
||||||
var subCmdsStandalone []*cli.Command = make([]*cli.Command, 0, 10)
|
var subCmdsStandalone []*cli.Command = make([]*cli.Command, 0, 10)
|
||||||
var subCmdWithConfig []*cli.Command = make([]*cli.Command, 0, 10)
|
var subCmdWithConfig []*cli.Command = make([]*cli.Command, 0, 10)
|
||||||
|
var globalFlags []cli.Flag = make([]cli.Flag, 0, 10)
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the executable is forgejo-cli, provide a Forgejo specific CLI
|
// If the executable is forgejo-cli, provide a Forgejo specific CLI
|
||||||
|
@ -131,6 +132,15 @@ func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
//
|
//
|
||||||
if executable == "forgejo-cli" {
|
if executable == "forgejo-cli" {
|
||||||
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background()))
|
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background()))
|
||||||
|
subCmdWithConfig = append(subCmdWithConfig, forgejo.CmdF3(context.Background()))
|
||||||
|
globalFlags = append(globalFlags, []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "quiet",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "verbose",
|
||||||
|
},
|
||||||
|
}...)
|
||||||
} else {
|
} else {
|
||||||
//
|
//
|
||||||
// Otherwise provide a Gitea compatible CLI which includes Forgejo
|
// Otherwise provide a Gitea compatible CLI which includes Forgejo
|
||||||
|
@ -142,10 +152,10 @@ func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
subCmdWithConfig = append(subCmdWithConfig, CmdActions)
|
subCmdWithConfig = append(subCmdWithConfig, CmdActions)
|
||||||
}
|
}
|
||||||
|
|
||||||
return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig)
|
return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig, globalFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command) *cli.App {
|
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command, globalFlagsArgs []cli.Flag) *cli.App {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.HelpName = "forgejo"
|
app.HelpName = "forgejo"
|
||||||
app.Name = "Forgejo"
|
app.Name = "Forgejo"
|
||||||
|
@ -185,6 +195,7 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd
|
||||||
app.DefaultCommand = CmdWeb.Name
|
app.DefaultCommand = CmdWeb.Name
|
||||||
|
|
||||||
globalFlags := appGlobalFlags()
|
globalFlags := appGlobalFlags()
|
||||||
|
globalFlags = append(globalFlags, globalFlagsArgs...)
|
||||||
app.Flags = append(app.Flags, cli.VersionFlag)
|
app.Flags = append(app.Flags, cli.VersionFlag)
|
||||||
app.Flags = append(app.Flags, globalFlags...)
|
app.Flags = append(app.Flags, globalFlags...)
|
||||||
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
||||||
|
|
|
@ -2499,6 +2499,15 @@ LEVEL = Info
|
||||||
;; If set to true, completely ignores server certificate validation errors. This option is unsafe.
|
;; If set to true, completely ignores server certificate validation errors. This option is unsafe.
|
||||||
;SKIP_TLS_VERIFY = false
|
;SKIP_TLS_VERIFY = false
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[F3]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;
|
||||||
|
;; Enable/Disable Friendly Forge Format (F3)
|
||||||
|
;ENABLED = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[federation]
|
;[federation]
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,6 +5,7 @@ go 1.22.0
|
||||||
toolchain go1.22.4
|
toolchain go1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
code.forgejo.org/f3/gof3/v3 v3.3.1
|
||||||
code.forgejo.org/forgejo/reply v1.0.2
|
code.forgejo.org/forgejo/reply v1.0.2
|
||||||
code.gitea.io/actions-proto-go v0.4.0
|
code.gitea.io/actions-proto-go v0.4.0
|
||||||
code.gitea.io/gitea-vet v0.2.3
|
code.gitea.io/gitea-vet v0.2.3
|
||||||
|
@ -202,6 +203,7 @@ require (
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/google/go-tpm v0.9.0 // indirect
|
github.com/google/go-tpm v0.9.0 // indirect
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,5 +1,7 @@
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
|
code.forgejo.org/f3/gof3/v3 v3.3.1 h1:lG7vT1Y/OIJDcZJE+mm5uycLyOQm0nVwGxnIWcBQ5XY=
|
||||||
|
code.forgejo.org/f3/gof3/v3 v3.3.1/go.mod h1:Wk0xt06c72MPbEplc3TZYYOTTHDlCR15YpgFG0ggci8=
|
||||||
code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE=
|
code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE=
|
||||||
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||||
code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ=
|
code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ=
|
||||||
|
|
22
modules/setting/f3.go
Normal file
22
modules/setting/f3.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Friendly Forge Format (F3) settings
|
||||||
|
var (
|
||||||
|
F3 = struct {
|
||||||
|
Enabled bool
|
||||||
|
}{
|
||||||
|
Enabled: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadF3From(rootCfg ConfigProvider) {
|
||||||
|
if err := rootCfg.Section("F3").MapTo(&F3); err != nil {
|
||||||
|
log.Fatal("Failed to map F3 settings: %v", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -224,6 +224,7 @@ func LoadSettings() {
|
||||||
loadProjectFrom(CfgProvider)
|
loadProjectFrom(CfgProvider)
|
||||||
loadMimeTypeMapFrom(CfgProvider)
|
loadMimeTypeMapFrom(CfgProvider)
|
||||||
loadFederationFrom(CfgProvider)
|
loadFederationFrom(CfgProvider)
|
||||||
|
loadF3From(CfgProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadSettingsForInstall initializes the settings for install
|
// LoadSettingsForInstall initializes the settings for install
|
||||||
|
|
171
services/f3/driver/asset.go
Normal file
171
services/f3/driver/asset.go
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"code.gitea.io/gitea/services/attachment"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &issue{}
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoAsset *repo_model.Attachment
|
||||||
|
sha string
|
||||||
|
contentType string
|
||||||
|
downloadFunc f3.DownloadFuncType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) SetNative(asset any) {
|
||||||
|
o.forgejoAsset = asset.(*repo_model.Attachment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoAsset.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoAsset == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &f3.ReleaseAsset{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
Name: o.forgejoAsset.Name,
|
||||||
|
ContentType: o.contentType,
|
||||||
|
Size: o.forgejoAsset.Size,
|
||||||
|
DownloadCount: o.forgejoAsset.DownloadCount,
|
||||||
|
Created: o.forgejoAsset.CreatedUnix.AsTime(),
|
||||||
|
SHA256: o.sha,
|
||||||
|
DownloadURL: o.forgejoAsset.DownloadURL(),
|
||||||
|
DownloadFunc: o.downloadFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) FromFormat(content f3.Interface) {
|
||||||
|
asset := content.(*f3.ReleaseAsset)
|
||||||
|
o.forgejoAsset = &repo_model.Attachment{
|
||||||
|
ID: f3_util.ParseInt(asset.GetID()),
|
||||||
|
Name: asset.Name,
|
||||||
|
Size: asset.Size,
|
||||||
|
DownloadCount: asset.DownloadCount,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(asset.Created.Unix()),
|
||||||
|
CustomDownloadURL: asset.DownloadURL,
|
||||||
|
}
|
||||||
|
o.contentType = asset.ContentType
|
||||||
|
o.sha = asset.SHA256
|
||||||
|
o.downloadFunc = asset.DownloadFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
asset, err := repo_model.GetAttachmentByID(ctx, id)
|
||||||
|
if repo_model.IsErrAttachmentNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("asset %v %w", id, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoAsset = asset
|
||||||
|
|
||||||
|
path := o.forgejoAsset.RelativePath()
|
||||||
|
|
||||||
|
{
|
||||||
|
f, err := storage.Attachments.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
hasher := sha256.New()
|
||||||
|
if _, err := io.Copy(hasher, f); err != nil {
|
||||||
|
panic(fmt.Errorf("io.Copy to hasher: %v", err))
|
||||||
|
}
|
||||||
|
o.sha = hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.downloadFunc = func() io.ReadCloser {
|
||||||
|
o.Trace("download %s from copy stored in temporary file %s", o.forgejoAsset.DownloadURL, path)
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoAsset.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoAsset.ID).Cols("name").Update(o.forgejoAsset); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateAssetCols: %v %v", o.forgejoAsset, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
uploader, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoAsset.UploaderID = uploader.ID
|
||||||
|
o.forgejoAsset.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
o.forgejoAsset.ReleaseID = f3_tree.GetReleaseID(o.GetNode())
|
||||||
|
o.forgejoAsset.UUID = uuid.New().String()
|
||||||
|
|
||||||
|
download := o.downloadFunc()
|
||||||
|
defer download.Close()
|
||||||
|
|
||||||
|
_, err = attachment.NewAttachment(ctx, o.forgejoAsset, download, o.forgejoAsset.Size)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Trace("asset created %d", o.forgejoAsset.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoAsset.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *asset) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
if err := repo_model.DeleteAttachment(ctx, o.forgejoAsset, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAsset() generic.NodeDriverInterface {
|
||||||
|
return &asset{}
|
||||||
|
}
|
42
services/f3/driver/assets.go
Normal file
42
services/f3/driver/assets.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type assets struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *assets) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
if page > 1 {
|
||||||
|
return generic.NewChildrenSlice(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseID := f3_tree.GetReleaseID(o.GetNode())
|
||||||
|
|
||||||
|
release, err := repo_model.GetReleaseByID(ctx, releaseID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetReleaseByID %v %w", releaseID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := release.LoadAttributes(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing assets: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(release.Attachments...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAssets() generic.NodeDriverInterface {
|
||||||
|
return &assets{}
|
||||||
|
}
|
122
services/f3/driver/comment.go
Normal file
122
services/f3/driver/comment.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &comment{}
|
||||||
|
|
||||||
|
type comment struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoComment *issues_model.Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) SetNative(comment any) {
|
||||||
|
o.forgejoComment = comment.(*issues_model.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoComment.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoComment == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Comment{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoComment.ID)),
|
||||||
|
PosterID: f3_tree.NewUserReference(o.forgejoComment.Poster.ID),
|
||||||
|
Content: o.forgejoComment.Content,
|
||||||
|
Created: o.forgejoComment.CreatedUnix.AsTime(),
|
||||||
|
Updated: o.forgejoComment.UpdatedUnix.AsTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) FromFormat(content f3.Interface) {
|
||||||
|
comment := content.(*f3.Comment)
|
||||||
|
|
||||||
|
o.forgejoComment = &issues_model.Comment{
|
||||||
|
ID: f3_util.ParseInt(comment.GetID()),
|
||||||
|
PosterID: comment.PosterID.GetIDAsInt(),
|
||||||
|
Poster: &user_model.User{
|
||||||
|
ID: comment.PosterID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
Content: comment.Content,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()),
|
||||||
|
UpdatedUnix: timeutil.TimeStamp(comment.Updated.Unix()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
comment, err := issues_model.GetCommentByID(ctx, id)
|
||||||
|
if issues_model.IsErrCommentNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("comment %v %w", id, err))
|
||||||
|
}
|
||||||
|
if err := comment.LoadPoster(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadPoster %v %w", *comment, err))
|
||||||
|
}
|
||||||
|
o.forgejoComment = comment
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoComment.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoComment.ID).Cols("content").Update(o.forgejoComment); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateCommentCols: %v %v", o.forgejoComment, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err := sess.NoAutoTime().Insert(o.forgejoComment); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("comment created %d", o.forgejoComment.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoComment.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comment) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
if err := issues_model.DeleteComment(ctx, o.forgejoComment); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newComment() generic.NodeDriverInterface {
|
||||||
|
return &comment{}
|
||||||
|
}
|
49
services/f3/driver/comments.go
Normal file
49
services/f3/driver/comments.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type comments struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *comments) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
commentable := f3_tree.GetCommentableID(o.GetNode())
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, commentable)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v %w", commentable, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx).
|
||||||
|
Table("comment").
|
||||||
|
Where("`issue_id` = ? AND `type` = ?", issue.ID, issues_model.CommentTypeComment)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
}
|
||||||
|
forgejoComments := make([]*issues_model.Comment, 0, pageSize)
|
||||||
|
if err := sess.Find(&forgejoComments); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing comments: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoComments...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newComments() generic.NodeDriverInterface {
|
||||||
|
return &comments{}
|
||||||
|
}
|
48
services/f3/driver/common.go
Normal file
48
services/f3/driver/common.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type common struct {
|
||||||
|
generic.NullDriver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) GetHelper() any {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
return generic.NewChildrenSlice(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) GetNativeID() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) SetNative(native any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) getTree() generic.TreeInterface {
|
||||||
|
return o.GetNode().GetTree()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) getPageSize() int {
|
||||||
|
return o.getTreeDriver().GetPageSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) getKind() generic.Kind {
|
||||||
|
return o.GetNode().GetKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) getTreeDriver() *treeDriver {
|
||||||
|
return o.GetTreeDriver().(*treeDriver)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *common) IsNull() bool { return false }
|
43
services/f3/driver/container.go
Normal file
43
services/f3/driver/container.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type container struct {
|
||||||
|
common
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) ToFormat() f3.Interface {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) FromFormat(content f3.Interface) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) Get(context.Context) bool { return true }
|
||||||
|
|
||||||
|
func (o *container) Put(ctx context.Context) generic.NodeID {
|
||||||
|
return o.upsert(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) Patch(ctx context.Context) {
|
||||||
|
o.upsert(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *container) upsert(context.Context) generic.NodeID {
|
||||||
|
return generic.NodeID(o.getKind())
|
||||||
|
}
|
64
services/f3/driver/forge.go
Normal file
64
services/f3/driver/forge.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type forge struct {
|
||||||
|
generic.NullDriver
|
||||||
|
|
||||||
|
ownersKind map[string]generic.Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func newForge() generic.NodeDriverInterface {
|
||||||
|
return &forge{
|
||||||
|
ownersKind: make(map[string]generic.Kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forge) getOwnersKind(ctx context.Context, id string) generic.Kind {
|
||||||
|
kind, ok := o.ownersKind[id]
|
||||||
|
if !ok {
|
||||||
|
user, err := user_model.GetUserByID(ctx, util.ParseInt(id))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("user_repo.GetUserByID: %w", err))
|
||||||
|
}
|
||||||
|
kind = f3_tree.KindUsers
|
||||||
|
if user.IsOrganization() {
|
||||||
|
kind = f3_tree.KindOrganization
|
||||||
|
}
|
||||||
|
o.ownersKind[id] = kind
|
||||||
|
}
|
||||||
|
return kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forge) getOwnersPath(ctx context.Context, id string) f3_tree.Path {
|
||||||
|
return f3_tree.NewPathFromString("/").SetForge().SetOwners(o.getOwnersKind(ctx, id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forge) Equals(context.Context, generic.NodeInterface) bool { return true }
|
||||||
|
func (o *forge) Get(context.Context) bool { return true }
|
||||||
|
func (o *forge) Put(context.Context) generic.NodeID { return generic.NodeID("forge") }
|
||||||
|
func (o *forge) Patch(context.Context) {}
|
||||||
|
func (o *forge) Delete(context.Context) {}
|
||||||
|
func (o *forge) NewFormat() f3.Interface { return &f3.Forge{} }
|
||||||
|
func (o *forge) FromFormat(f3.Interface) {}
|
||||||
|
|
||||||
|
func (o *forge) ToFormat() f3.Interface {
|
||||||
|
return &f3.Forge{
|
||||||
|
Common: f3.NewCommon("forge"),
|
||||||
|
URL: o.String(),
|
||||||
|
}
|
||||||
|
}
|
238
services/f3/driver/issue.go
Normal file
238
services/f3/driver/issue.go
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &issue{}
|
||||||
|
|
||||||
|
type issue struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoIssue *issues_model.Issue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) SetNative(issue any) {
|
||||||
|
o.forgejoIssue = issue.(*issues_model.Issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoIssue.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoIssue == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
var milestone *f3.Reference
|
||||||
|
if o.forgejoIssue.Milestone != nil {
|
||||||
|
milestone = f3_tree.NewIssueMilestoneReference(o.forgejoIssue.Milestone.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
assignees := make([]*f3.Reference, 0, len(o.forgejoIssue.Assignees))
|
||||||
|
for _, assignee := range o.forgejoIssue.Assignees {
|
||||||
|
assignees = append(assignees, f3_tree.NewUserReference(assignee.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]*f3.Reference, 0, len(o.forgejoIssue.Labels))
|
||||||
|
for _, label := range o.forgejoIssue.Labels {
|
||||||
|
labels = append(labels, f3_tree.NewIssueLabelReference(label.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &f3.Issue{
|
||||||
|
Title: o.forgejoIssue.Title,
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
PosterID: f3_tree.NewUserReference(o.forgejoIssue.Poster.ID),
|
||||||
|
Assignees: assignees,
|
||||||
|
Labels: labels,
|
||||||
|
Content: o.forgejoIssue.Content,
|
||||||
|
Milestone: milestone,
|
||||||
|
State: string(o.forgejoIssue.State()),
|
||||||
|
Created: o.forgejoIssue.CreatedUnix.AsTime(),
|
||||||
|
Updated: o.forgejoIssue.UpdatedUnix.AsTime(),
|
||||||
|
Closed: o.forgejoIssue.ClosedUnix.AsTimePtr(),
|
||||||
|
IsLocked: o.forgejoIssue.IsLocked,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) FromFormat(content f3.Interface) {
|
||||||
|
issue := content.(*f3.Issue)
|
||||||
|
var milestone *issues_model.Milestone
|
||||||
|
if issue.Milestone != nil {
|
||||||
|
milestone = &issues_model.Milestone{
|
||||||
|
ID: issue.Milestone.GetIDAsInt(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.forgejoIssue = &issues_model.Issue{
|
||||||
|
Title: issue.Title,
|
||||||
|
Index: f3_util.ParseInt(issue.GetID()),
|
||||||
|
PosterID: issue.PosterID.GetIDAsInt(),
|
||||||
|
Poster: &user_model.User{
|
||||||
|
ID: issue.PosterID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
Content: issue.Content,
|
||||||
|
Milestone: milestone,
|
||||||
|
IsClosed: issue.State == "closed",
|
||||||
|
CreatedUnix: timeutil.TimeStamp(issue.Created.Unix()),
|
||||||
|
UpdatedUnix: timeutil.TimeStamp(issue.Updated.Unix()),
|
||||||
|
IsLocked: issue.IsLocked,
|
||||||
|
}
|
||||||
|
|
||||||
|
assignees := make([]*user_model.User, 0, len(issue.Assignees))
|
||||||
|
for _, assignee := range issue.Assignees {
|
||||||
|
assignees = append(assignees, &user_model.User{ID: assignee.GetIDAsInt()})
|
||||||
|
}
|
||||||
|
o.forgejoIssue.Assignees = assignees
|
||||||
|
|
||||||
|
labels := make([]*issues_model.Label, 0, len(issue.Labels))
|
||||||
|
for _, label := range issue.Labels {
|
||||||
|
labels = append(labels, &issues_model.Label{ID: label.GetIDAsInt()})
|
||||||
|
}
|
||||||
|
o.forgejoIssue.Labels = labels
|
||||||
|
|
||||||
|
if issue.Closed != nil {
|
||||||
|
o.forgejoIssue.ClosedUnix = timeutil.TimeStamp(issue.Closed.Unix())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, id)
|
||||||
|
if issues_model.IsErrIssueNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("issue %v %w", id, err))
|
||||||
|
}
|
||||||
|
if err := issue.LoadAttributes(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoIssue = issue
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) Patch(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
o.Trace("repo_id = %d, index = %d", project, id)
|
||||||
|
if _, err := db.GetEngine(ctx).Where("`repo_id` = ? AND `index` = ?", project, id).Cols("name", "content").Update(o.forgejoIssue); err != nil {
|
||||||
|
panic(fmt.Errorf("%v %v", o.forgejoIssue, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
o.forgejoIssue.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
makeLabels := func(issueID int64) []issues_model.IssueLabel {
|
||||||
|
labels := make([]issues_model.IssueLabel, 0, len(o.forgejoIssue.Labels))
|
||||||
|
for _, label := range o.forgejoIssue.Labels {
|
||||||
|
o.Trace("%d with label %d", issueID, label.ID)
|
||||||
|
labels = append(labels, issues_model.IssueLabel{
|
||||||
|
IssueID: issueID,
|
||||||
|
LabelID: label.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := db.GetNextResourceIndex(ctx, "issue_index", o.forgejoIssue.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("generate issue index failed: %w", err))
|
||||||
|
}
|
||||||
|
o.forgejoIssue.Index = idx
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err = sess.NoAutoTime().Insert(o.forgejoIssue); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := makeLabels(o.forgejoIssue.ID)
|
||||||
|
if len(labels) > 0 {
|
||||||
|
if _, err := sess.Insert(labels); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeAssignees := func(issueID int64) []issues_model.IssueAssignees {
|
||||||
|
assignees := make([]issues_model.IssueAssignees, 0, len(o.forgejoIssue.Assignees))
|
||||||
|
for _, assignee := range o.forgejoIssue.Assignees {
|
||||||
|
o.Trace("%d with assignee %d", issueID, assignee.ID)
|
||||||
|
assignees = append(assignees, issues_model.IssueAssignees{
|
||||||
|
IssueID: issueID,
|
||||||
|
AssigneeID: assignee.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return assignees
|
||||||
|
}
|
||||||
|
|
||||||
|
assignees := makeAssignees(o.forgejoIssue.ID)
|
||||||
|
if len(assignees) > 0 {
|
||||||
|
if _, err := sess.Insert(assignees); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Trace("issue created %d/%d", o.forgejoIssue.ID, o.forgejoIssue.Index)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoIssue.Index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issue) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
owner := f3_tree.GetOwnerName(o.GetNode())
|
||||||
|
project := f3_tree.GetProjectName(o.GetNode())
|
||||||
|
repoPath := repo_model.RepoPath(owner, project)
|
||||||
|
gitRepo, err := git.OpenRepository(ctx, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := issue_service.DeleteIssue(ctx, doer, gitRepo, o.forgejoIssue); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIssue() generic.NodeDriverInterface {
|
||||||
|
return &issue{}
|
||||||
|
}
|
40
services/f3/driver/issues.go
Normal file
40
services/f3/driver/issues.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type issues struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *issues) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
forgejoIssues, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
|
||||||
|
Paginator: &db.ListOptions{Page: page, PageSize: pageSize},
|
||||||
|
RepoIDs: []int64{project},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing issues: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoIssues...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIssues() generic.NodeDriverInterface {
|
||||||
|
return &issues{}
|
||||||
|
}
|
113
services/f3/driver/label.go
Normal file
113
services/f3/driver/label.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &label{}
|
||||||
|
|
||||||
|
type label struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoLabel *issues_model.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) SetNative(label any) {
|
||||||
|
o.forgejoLabel = label.(*issues_model.Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoLabel.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoLabel == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Label{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoLabel.ID)),
|
||||||
|
Name: o.forgejoLabel.Name,
|
||||||
|
Color: o.forgejoLabel.Color,
|
||||||
|
Description: o.forgejoLabel.Description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) FromFormat(content f3.Interface) {
|
||||||
|
label := content.(*f3.Label)
|
||||||
|
o.forgejoLabel = &issues_model.Label{
|
||||||
|
ID: f3_util.ParseInt(label.GetID()),
|
||||||
|
Name: label.Name,
|
||||||
|
Description: label.Description,
|
||||||
|
Color: label.Color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
label, err := issues_model.GetLabelInRepoByID(ctx, project, id)
|
||||||
|
if issues_model.IsErrRepoLabelNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("label %v %w", id, err))
|
||||||
|
}
|
||||||
|
o.forgejoLabel = label
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoLabel.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoLabel.ID).Cols("name", "description").Update(o.forgejoLabel); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateLabelCols: %v %v", o.forgejoLabel, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
o.forgejoLabel.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
if err := issues_model.NewLabel(ctx, o.forgejoLabel); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("label created %d", o.forgejoLabel.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoLabel.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *label) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
if err := issues_model.DeleteLabel(ctx, project, o.forgejoLabel.ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLabel() generic.NodeDriverInterface {
|
||||||
|
return &label{}
|
||||||
|
}
|
37
services/f3/driver/labels.go
Normal file
37
services/f3/driver/labels.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type labels struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *labels) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
forgejoLabels, err := issues_model.GetLabelsByRepoID(ctx, project, "", db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing labels: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoLabels...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLabels() generic.NodeDriverInterface {
|
||||||
|
return &labels{}
|
||||||
|
}
|
17
services/f3/driver/main.go
Normal file
17
services/f3/driver/main.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
f3_tree.RegisterForgeFactory(driver_options.Name, newTreeDriver)
|
||||||
|
options.RegisterFactory(driver_options.Name, newOptions)
|
||||||
|
}
|
30
services/f3/driver/main_test.go
Normal file
30
services/f3/driver/main_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
_ "code.gitea.io/gitea/models"
|
||||||
|
_ "code.gitea.io/gitea/models/actions"
|
||||||
|
_ "code.gitea.io/gitea/models/activities"
|
||||||
|
_ "code.gitea.io/gitea/models/perm/access"
|
||||||
|
_ "code.gitea.io/gitea/services/f3/driver/tests"
|
||||||
|
|
||||||
|
tests_f3 "code.forgejo.org/f3/gof3/v3/tree/tests/f3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestF3(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
tests_f3.ForgeCompliance(t, driver_options.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
unittest.MainTest(m)
|
||||||
|
}
|
150
services/f3/driver/milestone.go
Normal file
150
services/f3/driver/milestone.go
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &milestone{}
|
||||||
|
|
||||||
|
type milestone struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoMilestone *issues_model.Milestone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) SetNative(milestone any) {
|
||||||
|
o.forgejoMilestone = milestone.(*issues_model.Milestone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoMilestone.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoMilestone == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Milestone{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoMilestone.ID)),
|
||||||
|
Title: o.forgejoMilestone.Name,
|
||||||
|
Description: o.forgejoMilestone.Content,
|
||||||
|
Created: o.forgejoMilestone.CreatedUnix.AsTime(),
|
||||||
|
Updated: o.forgejoMilestone.UpdatedUnix.AsTimePtr(),
|
||||||
|
Deadline: o.forgejoMilestone.DeadlineUnix.AsTimePtr(),
|
||||||
|
State: string(o.forgejoMilestone.State()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) FromFormat(content f3.Interface) {
|
||||||
|
milestone := content.(*f3.Milestone)
|
||||||
|
|
||||||
|
var deadline timeutil.TimeStamp
|
||||||
|
if milestone.Deadline != nil {
|
||||||
|
deadline = timeutil.TimeStamp(milestone.Deadline.Unix())
|
||||||
|
}
|
||||||
|
if deadline == 0 {
|
||||||
|
deadline = timeutil.TimeStamp(time.Date(9999, 1, 1, 0, 0, 0, 0, setting.DefaultUILocation).Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
var closed timeutil.TimeStamp
|
||||||
|
if milestone.Closed != nil {
|
||||||
|
closed = timeutil.TimeStamp(milestone.Closed.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
if milestone.Created.IsZero() {
|
||||||
|
if milestone.Updated != nil {
|
||||||
|
milestone.Created = *milestone.Updated
|
||||||
|
} else if milestone.Deadline != nil {
|
||||||
|
milestone.Created = *milestone.Deadline
|
||||||
|
} else {
|
||||||
|
milestone.Created = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if milestone.Updated == nil || milestone.Updated.IsZero() {
|
||||||
|
milestone.Updated = &milestone.Created
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoMilestone = &issues_model.Milestone{
|
||||||
|
ID: f3_util.ParseInt(milestone.GetID()),
|
||||||
|
Name: milestone.Title,
|
||||||
|
Content: milestone.Description,
|
||||||
|
IsClosed: milestone.State == "closed",
|
||||||
|
CreatedUnix: timeutil.TimeStamp(milestone.Created.Unix()),
|
||||||
|
UpdatedUnix: timeutil.TimeStamp(milestone.Updated.Unix()),
|
||||||
|
ClosedDateUnix: closed,
|
||||||
|
DeadlineUnix: deadline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
milestone, err := issues_model.GetMilestoneByRepoID(ctx, project, id)
|
||||||
|
if issues_model.IsErrMilestoneNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("milestone %v %w", id, err))
|
||||||
|
}
|
||||||
|
o.forgejoMilestone = milestone
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoMilestone.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoMilestone.ID).Cols("name", "description").Update(o.forgejoMilestone); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateMilestoneCols: %v %v", o.forgejoMilestone, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
o.forgejoMilestone.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
if err := issues_model.NewMilestone(ctx, o.forgejoMilestone); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("milestone created %d", o.forgejoMilestone.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoMilestone.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestone) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
if err := issues_model.DeleteMilestoneByRepoID(ctx, project, o.forgejoMilestone.ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMilestone() generic.NodeDriverInterface {
|
||||||
|
return &milestone{}
|
||||||
|
}
|
40
services/f3/driver/milestones.go
Normal file
40
services/f3/driver/milestones.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type milestones struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *milestones) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
forgejoMilestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||||
|
ListOptions: db.ListOptions{Page: page, PageSize: pageSize},
|
||||||
|
RepoID: project,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing milestones: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoMilestones...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMilestones() generic.NodeDriverInterface {
|
||||||
|
return &milestones{}
|
||||||
|
}
|
20
services/f3/driver/options.go
Normal file
20
services/f3/driver/options.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newOptions() options.Interface {
|
||||||
|
o := &driver_options.Options{}
|
||||||
|
o.SetName(driver_options.Name)
|
||||||
|
o.SetNewMigrationHTTPClient(func() *http.Client { return &http.Client{} })
|
||||||
|
return o
|
||||||
|
}
|
7
services/f3/driver/options/name.go
Normal file
7
services/f3/driver/options/name.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package options
|
||||||
|
|
||||||
|
const Name = "internal_forgejo"
|
31
services/f3/driver/options/options.go
Normal file
31
services/f3/driver/options/options.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options/cli"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NewMigrationHTTPClientFun func() *http.Client
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
options.Options
|
||||||
|
logger.OptionsLogger
|
||||||
|
cli.OptionsCLI
|
||||||
|
|
||||||
|
NewMigrationHTTPClient NewMigrationHTTPClientFun
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) GetNewMigrationHTTPClient() NewMigrationHTTPClientFun {
|
||||||
|
return o.NewMigrationHTTPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) SetNewMigrationHTTPClient(fun NewMigrationHTTPClientFun) {
|
||||||
|
o.NewMigrationHTTPClient = fun
|
||||||
|
}
|
111
services/f3/driver/organization.go
Normal file
111
services/f3/driver/organization.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &organization{}
|
||||||
|
|
||||||
|
type organization struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoOrganization *org_model.Organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) SetNative(organization any) {
|
||||||
|
o.forgejoOrganization = organization.(*org_model.Organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoOrganization.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoOrganization == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Organization{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoOrganization.ID)),
|
||||||
|
Name: o.forgejoOrganization.Name,
|
||||||
|
FullName: o.forgejoOrganization.FullName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) FromFormat(content f3.Interface) {
|
||||||
|
organization := content.(*f3.Organization)
|
||||||
|
o.forgejoOrganization = &org_model.Organization{
|
||||||
|
ID: f3_util.ParseInt(organization.GetID()),
|
||||||
|
Name: organization.Name,
|
||||||
|
FullName: organization.FullName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
organization, err := org_model.GetOrgByID(ctx, id)
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("organization %v %w", id, err))
|
||||||
|
}
|
||||||
|
o.forgejoOrganization = organization
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoOrganization.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoOrganization.ID).Cols("full_name").Update(o.forgejoOrganization); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateOrganizationCols: %v %v", o.forgejoOrganization, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
err = org_model.CreateOrganization(ctx, o.forgejoOrganization, doer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoOrganization.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organization) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
if err := org_model.DeleteOrganization(ctx, o.forgejoOrganization); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrganization() generic.NodeDriverInterface {
|
||||||
|
return &organization{}
|
||||||
|
}
|
50
services/f3/driver/organizations.go
Normal file
50
services/f3/driver/organizations.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
org_model "code.gitea.io/gitea/models/organization"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type organizations struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizations) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: o.getPageSize()})
|
||||||
|
}
|
||||||
|
sess = sess.Select("`user`.*").
|
||||||
|
Where("`type`=?", user_model.UserTypeOrganization)
|
||||||
|
organizations := make([]*org_model.Organization, 0, o.getPageSize())
|
||||||
|
|
||||||
|
if err := sess.Find(&organizations); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing organizations: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(organizations...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *organizations) GetIDFromName(ctx context.Context, name string) generic.NodeID {
|
||||||
|
organization, err := org_model.GetOrgByName(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetOrganizationByName: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", organization.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrganizations() generic.NodeDriverInterface {
|
||||||
|
return &organizations{}
|
||||||
|
}
|
188
services/f3/driver/project.go
Normal file
188
services/f3/driver/project.go
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &project{}
|
||||||
|
|
||||||
|
type project struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoProject *repo_model.Repository
|
||||||
|
forked *f3.Reference
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) SetNative(project any) {
|
||||||
|
o.forgejoProject = project.(*repo_model.Repository)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoProject.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) setForkedReference(ctx context.Context) {
|
||||||
|
if !o.forgejoProject.IsFork {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := o.forgejoProject.GetBaseRepo(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("GetBaseRepo %v %w", o.forgejoProject, err))
|
||||||
|
}
|
||||||
|
forkParent := o.forgejoProject.BaseRepo
|
||||||
|
if err := forkParent.LoadOwner(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadOwner %v %w", forkParent, err))
|
||||||
|
}
|
||||||
|
owners := "users"
|
||||||
|
if forkParent.Owner.IsOrganization() {
|
||||||
|
owners = "organizations"
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forked = f3_tree.NewProjectReference(owners, fmt.Sprintf("%d", forkParent.Owner.ID), fmt.Sprintf("%d", forkParent.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoProject == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Project{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoProject.ID)),
|
||||||
|
Name: o.forgejoProject.Name,
|
||||||
|
IsPrivate: o.forgejoProject.IsPrivate,
|
||||||
|
IsMirror: o.forgejoProject.IsMirror,
|
||||||
|
Description: o.forgejoProject.Description,
|
||||||
|
DefaultBranch: o.forgejoProject.DefaultBranch,
|
||||||
|
Forked: o.forked,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) FromFormat(content f3.Interface) {
|
||||||
|
project := content.(*f3.Project)
|
||||||
|
o.forgejoProject = &repo_model.Repository{
|
||||||
|
ID: f3_util.ParseInt(project.GetID()),
|
||||||
|
Name: project.Name,
|
||||||
|
IsPrivate: project.IsPrivate,
|
||||||
|
IsMirror: project.IsMirror,
|
||||||
|
Description: project.Description,
|
||||||
|
DefaultBranch: project.DefaultBranch,
|
||||||
|
}
|
||||||
|
if project.Forked != nil {
|
||||||
|
o.forgejoProject.IsFork = true
|
||||||
|
o.forgejoProject.ForkID = project.Forked.GetIDAsInt()
|
||||||
|
}
|
||||||
|
o.forked = project.Forked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
u, err := repo_model.GetRepositoryByID(ctx, id)
|
||||||
|
if repo_model.IsErrRepoNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("project %v %w", id, err))
|
||||||
|
}
|
||||||
|
o.forgejoProject = u
|
||||||
|
o.setForkedReference(ctx)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoProject.ID)
|
||||||
|
o.forgejoProject.LowerName = strings.ToLower(o.forgejoProject.Name)
|
||||||
|
if err := repo_model.UpdateRepositoryCols(ctx, o.forgejoProject,
|
||||||
|
"description",
|
||||||
|
"name",
|
||||||
|
"lower_name",
|
||||||
|
); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateRepositoryCols: %v %v", o.forgejoProject, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
ownerID := f3_tree.GetOwnerID(o.GetNode())
|
||||||
|
owner, err := user_model.GetUserByID(ctx, ownerID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetUserByID %v %w", ownerID, err))
|
||||||
|
}
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.forked == nil {
|
||||||
|
repo, err := repo_service.CreateRepositoryDirectly(ctx, doer, owner, repo_service.CreateRepoOptions{
|
||||||
|
Name: o.forgejoProject.Name,
|
||||||
|
Description: o.forgejoProject.Description,
|
||||||
|
IsPrivate: o.forgejoProject.IsPrivate,
|
||||||
|
DefaultBranch: o.forgejoProject.DefaultBranch,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.forgejoProject = repo
|
||||||
|
o.Trace("project created %d", o.forgejoProject.ID)
|
||||||
|
} else {
|
||||||
|
if err = o.forgejoProject.GetBaseRepo(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("GetBaseRepo %v %w", o.forgejoProject, err))
|
||||||
|
}
|
||||||
|
if err = o.forgejoProject.BaseRepo.LoadOwner(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadOwner %v %w", o.forgejoProject.BaseRepo, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := repo_service.ForkRepository(ctx, doer, owner, repo_service.ForkRepoOptions{
|
||||||
|
BaseRepo: o.forgejoProject.BaseRepo,
|
||||||
|
Name: o.forgejoProject.Name,
|
||||||
|
Description: o.forgejoProject.Description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.forgejoProject = repo
|
||||||
|
o.Trace("project created %d", o.forgejoProject.ID)
|
||||||
|
}
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoProject.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *project) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo_service.DeleteRepository(ctx, doer, o.forgejoProject, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProject() generic.NodeDriverInterface {
|
||||||
|
return &project{}
|
||||||
|
}
|
56
services/f3/driver/projects.go
Normal file
56
services/f3/driver/projects.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type projects struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *projects) GetIDFromName(ctx context.Context, name string) generic.NodeID {
|
||||||
|
owner := f3_tree.GetOwnerName(o.GetNode())
|
||||||
|
forgejoProject, err := repo_model.GetRepositoryByOwnerAndName(ctx, owner, name)
|
||||||
|
if repo_model.IsErrRepoNotExist(err) {
|
||||||
|
return generic.NilID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error GetRepositoryByOwnerAndName(%s, %s): %v", owner, name, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", forgejoProject.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *projects) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
owner := f3_tree.GetOwner(o.GetNode())
|
||||||
|
|
||||||
|
forgejoProjects, _, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
|
||||||
|
ListOptions: db.ListOptions{Page: page, PageSize: pageSize},
|
||||||
|
OwnerID: f3_util.ParseInt(string(owner.GetID())),
|
||||||
|
Private: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing projects: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoProjects...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProjects() generic.NodeDriverInterface {
|
||||||
|
return &projects{}
|
||||||
|
}
|
320
services/f3/driver/pullrequest.go
Normal file
320
services/f3/driver/pullrequest.go
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &pullRequest{}
|
||||||
|
|
||||||
|
type pullRequest struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoPullRequest *issues_model.Issue
|
||||||
|
headRepository *f3.Reference
|
||||||
|
baseRepository *f3.Reference
|
||||||
|
fetchFunc f3.PullRequestFetchFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) SetNative(pullRequest any) {
|
||||||
|
o.forgejoPullRequest = pullRequest.(*issues_model.Issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoPullRequest.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) repositoryToReference(ctx context.Context, repository *repo_model.Repository) *f3.Reference {
|
||||||
|
if repository == nil {
|
||||||
|
panic("unexpected nil repository")
|
||||||
|
}
|
||||||
|
forge := o.getTree().GetRoot().GetChild(f3_tree.KindForge).GetDriver().(*forge)
|
||||||
|
owners := forge.getOwnersPath(ctx, fmt.Sprintf("%d", repository.OwnerID))
|
||||||
|
return f3_tree.NewRepositoryReference(owners.String(), repository.OwnerID, repository.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) referenceToRepository(reference *f3.Reference) int64 {
|
||||||
|
var project int64
|
||||||
|
if reference.Get() == "../../repository/vcs" {
|
||||||
|
project = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
} else {
|
||||||
|
p := f3_tree.ToPath(generic.PathAbsolute(o.GetNode().GetCurrentPath().String(), reference.Get()))
|
||||||
|
o.Trace("%v %v", o.GetNode().GetCurrentPath().String(), p)
|
||||||
|
_, project = p.OwnerAndProjectID()
|
||||||
|
}
|
||||||
|
return project
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoPullRequest == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
var milestone *f3.Reference
|
||||||
|
if o.forgejoPullRequest.Milestone != nil {
|
||||||
|
milestone = f3_tree.NewIssueMilestoneReference(o.forgejoPullRequest.Milestone.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mergedTime *time.Time
|
||||||
|
if o.forgejoPullRequest.PullRequest.HasMerged {
|
||||||
|
mergedTime = o.forgejoPullRequest.PullRequest.MergedUnix.AsTimePtr()
|
||||||
|
}
|
||||||
|
|
||||||
|
var closedTime *time.Time
|
||||||
|
if o.forgejoPullRequest.IsClosed {
|
||||||
|
closedTime = o.forgejoPullRequest.ClosedUnix.AsTimePtr()
|
||||||
|
}
|
||||||
|
|
||||||
|
makePullRequestBranch := func(repo *repo_model.Repository, branch string) f3.PullRequestBranch {
|
||||||
|
r, err := git.OpenRepository(context.Background(), repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
b, err := r.GetBranch(branch)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := b.GetCommit()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3.PullRequestBranch{
|
||||||
|
Ref: branch,
|
||||||
|
SHA: c.ID.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := o.forgejoPullRequest.PullRequest.LoadHeadRepo(db.DefaultContext); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
head := makePullRequestBranch(o.forgejoPullRequest.PullRequest.HeadRepo, o.forgejoPullRequest.PullRequest.HeadBranch)
|
||||||
|
head.Repository = o.headRepository
|
||||||
|
if err := o.forgejoPullRequest.PullRequest.LoadBaseRepo(db.DefaultContext); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
base := makePullRequestBranch(o.forgejoPullRequest.PullRequest.BaseRepo, o.forgejoPullRequest.PullRequest.BaseBranch)
|
||||||
|
base.Repository = o.baseRepository
|
||||||
|
|
||||||
|
return &f3.PullRequest{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
PosterID: f3_tree.NewUserReference(o.forgejoPullRequest.Poster.ID),
|
||||||
|
Title: o.forgejoPullRequest.Title,
|
||||||
|
Content: o.forgejoPullRequest.Content,
|
||||||
|
Milestone: milestone,
|
||||||
|
State: string(o.forgejoPullRequest.State()),
|
||||||
|
IsLocked: o.forgejoPullRequest.IsLocked,
|
||||||
|
Created: o.forgejoPullRequest.CreatedUnix.AsTime(),
|
||||||
|
Updated: o.forgejoPullRequest.UpdatedUnix.AsTime(),
|
||||||
|
Closed: closedTime,
|
||||||
|
Merged: o.forgejoPullRequest.PullRequest.HasMerged,
|
||||||
|
MergedTime: mergedTime,
|
||||||
|
MergeCommitSHA: o.forgejoPullRequest.PullRequest.MergedCommitID,
|
||||||
|
Head: head,
|
||||||
|
Base: base,
|
||||||
|
FetchFunc: o.fetchFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) FromFormat(content f3.Interface) {
|
||||||
|
pullRequest := content.(*f3.PullRequest)
|
||||||
|
var milestone *issues_model.Milestone
|
||||||
|
if pullRequest.Milestone != nil {
|
||||||
|
milestone = &issues_model.Milestone{
|
||||||
|
ID: pullRequest.Milestone.GetIDAsInt(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o.headRepository = pullRequest.Head.Repository
|
||||||
|
o.baseRepository = pullRequest.Base.Repository
|
||||||
|
pr := issues_model.PullRequest{
|
||||||
|
HeadBranch: pullRequest.Head.Ref,
|
||||||
|
HeadRepoID: o.referenceToRepository(o.headRepository),
|
||||||
|
BaseBranch: pullRequest.Base.Ref,
|
||||||
|
BaseRepoID: o.referenceToRepository(o.baseRepository),
|
||||||
|
|
||||||
|
MergeBase: pullRequest.Base.SHA,
|
||||||
|
Index: f3_util.ParseInt(pullRequest.GetID()),
|
||||||
|
HasMerged: pullRequest.Merged,
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoPullRequest = &issues_model.Issue{
|
||||||
|
Index: f3_util.ParseInt(pullRequest.GetID()),
|
||||||
|
PosterID: pullRequest.PosterID.GetIDAsInt(),
|
||||||
|
Poster: &user_model.User{
|
||||||
|
ID: pullRequest.PosterID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
Title: pullRequest.Title,
|
||||||
|
Content: pullRequest.Content,
|
||||||
|
Milestone: milestone,
|
||||||
|
IsClosed: pullRequest.State == "closed",
|
||||||
|
CreatedUnix: timeutil.TimeStamp(pullRequest.Created.Unix()),
|
||||||
|
UpdatedUnix: timeutil.TimeStamp(pullRequest.Updated.Unix()),
|
||||||
|
IsLocked: pullRequest.IsLocked,
|
||||||
|
PullRequest: &pr,
|
||||||
|
IsPull: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pullRequest.Closed != nil {
|
||||||
|
o.forgejoPullRequest.ClosedUnix = timeutil.TimeStamp(pullRequest.Closed.Unix())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, id)
|
||||||
|
if issues_model.IsErrIssueNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("issue %v %w", id, err))
|
||||||
|
}
|
||||||
|
if err := issue.LoadAttributes(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := issue.PullRequest.LoadHeadRepo(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.headRepository = o.repositoryToReference(ctx, issue.PullRequest.HeadRepo)
|
||||||
|
if err := issue.PullRequest.LoadBaseRepo(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.baseRepository = o.repositoryToReference(ctx, issue.PullRequest.BaseRepo)
|
||||||
|
|
||||||
|
o.forgejoPullRequest = issue
|
||||||
|
o.Trace("ID = %s", o.forgejoPullRequest.ID)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) Patch(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
o.Trace("repo_id = %d, index = %d", project, id)
|
||||||
|
if _, err := db.GetEngine(ctx).Where("`repo_id` = ? AND `index` = ?", project, id).Cols("name", "content").Update(o.forgejoPullRequest); err != nil {
|
||||||
|
panic(fmt.Errorf("%v %v", o.forgejoPullRequest, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) GetPullRequestPushRefs() []string {
|
||||||
|
return []string{
|
||||||
|
fmt.Sprintf("refs/f3/%s/head", o.GetNativeID()),
|
||||||
|
fmt.Sprintf("refs/pull/%s/head", o.GetNativeID()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) GetPullRequestRef() string {
|
||||||
|
return fmt.Sprintf("refs/pull/%s/head", o.GetNativeID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
o.forgejoPullRequest.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
ctx, committer, err := db.TxContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer committer.Close()
|
||||||
|
|
||||||
|
idx, err := db.GetNextResourceIndex(ctx, "issue_index", o.forgejoPullRequest.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("generate issue index failed: %w", err))
|
||||||
|
}
|
||||||
|
o.forgejoPullRequest.Index = idx
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err = sess.NoAutoTime().Insert(o.forgejoPullRequest); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := o.forgejoPullRequest.PullRequest
|
||||||
|
pr.Index = o.forgejoPullRequest.Index
|
||||||
|
pr.IssueID = o.forgejoPullRequest.ID
|
||||||
|
pr.HeadRepoID = o.referenceToRepository(o.headRepository)
|
||||||
|
if pr.HeadRepoID == 0 {
|
||||||
|
panic(fmt.Errorf("HeadRepoID == 0 in %v", pr))
|
||||||
|
}
|
||||||
|
pr.BaseRepoID = o.referenceToRepository(o.baseRepository)
|
||||||
|
if pr.BaseRepoID == 0 {
|
||||||
|
panic(fmt.Errorf("BaseRepoID == 0 in %v", pr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = sess.NoAutoTime().Insert(pr); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = committer.Commit(); err != nil {
|
||||||
|
panic(fmt.Errorf("Commit: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := pr.LoadHeadRepo(ctx); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Trace("pullRequest created %d/%d", o.forgejoPullRequest.ID, o.forgejoPullRequest.Index)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoPullRequest.Index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequest) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
owner := f3_tree.GetOwnerName(o.GetNode())
|
||||||
|
project := f3_tree.GetProjectName(o.GetNode())
|
||||||
|
repoPath := repo_model.RepoPath(owner, project)
|
||||||
|
gitRepo, err := git.OpenRepository(ctx, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := issue_service.DeleteIssue(ctx, doer, gitRepo, o.forgejoPullRequest); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPullRequest() generic.NodeDriverInterface {
|
||||||
|
return &pullRequest{}
|
||||||
|
}
|
42
services/f3/driver/pullrequests.go
Normal file
42
services/f3/driver/pullrequests.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pullRequests struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *pullRequests) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
forgejoPullRequests, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
|
||||||
|
Paginator: &db.ListOptions{Page: page, PageSize: pageSize},
|
||||||
|
RepoIDs: []int64{project},
|
||||||
|
IsPull: optional.Some(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing pullRequests: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoPullRequests...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPullRequests() generic.NodeDriverInterface {
|
||||||
|
return &pullRequests{}
|
||||||
|
}
|
133
services/f3/driver/reaction.go
Normal file
133
services/f3/driver/reaction.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &reaction{}
|
||||||
|
|
||||||
|
type reaction struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoReaction *issues_model.Reaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) SetNative(reaction any) {
|
||||||
|
o.forgejoReaction = reaction.(*issues_model.Reaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoReaction.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoReaction == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Reaction{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoReaction.ID)),
|
||||||
|
UserID: f3_tree.NewUserReference(o.forgejoReaction.User.ID),
|
||||||
|
Content: o.forgejoReaction.Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) FromFormat(content f3.Interface) {
|
||||||
|
reaction := content.(*f3.Reaction)
|
||||||
|
|
||||||
|
o.forgejoReaction = &issues_model.Reaction{
|
||||||
|
ID: f3_util.ParseInt(reaction.GetID()),
|
||||||
|
UserID: reaction.UserID.GetIDAsInt(),
|
||||||
|
User: &user_model.User{
|
||||||
|
ID: reaction.UserID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
Type: reaction.Content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
if has, err := db.GetEngine(ctx).Where("ID = ?", id).Get(o.forgejoReaction); err != nil {
|
||||||
|
panic(fmt.Errorf("reaction %v %w", id, err))
|
||||||
|
} else if !has {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := o.forgejoReaction.LoadUser(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadUser %v %w", *o.forgejoReaction, err))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoReaction.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoReaction.ID).Cols("type").Update(o.forgejoReaction); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateReactionCols: %v %v", o.forgejoReaction, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) Put(ctx context.Context) generic.NodeID {
|
||||||
|
o.Error("%v", o.forgejoReaction.User)
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
reactionable := f3_tree.GetReactionable(o.GetNode())
|
||||||
|
reactionableID := f3_tree.GetReactionableID(o.GetNode())
|
||||||
|
|
||||||
|
switch reactionable.GetKind() {
|
||||||
|
case f3_tree.KindIssue, f3_tree.KindPullRequest:
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, reactionableID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v %w", reactionableID, err))
|
||||||
|
}
|
||||||
|
o.forgejoReaction.IssueID = issue.ID
|
||||||
|
case f3_tree.KindComment:
|
||||||
|
o.forgejoReaction.CommentID = reactionableID
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected type %v", reactionable.GetKind()))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.Error("%v", o.forgejoReaction)
|
||||||
|
|
||||||
|
if _, err := sess.Insert(o.forgejoReaction); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("reaction created %d", o.forgejoReaction.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoReaction.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reaction) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
if _, err := sess.Delete(o.forgejoReaction); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReaction() generic.NodeDriverInterface {
|
||||||
|
return &reaction{}
|
||||||
|
}
|
59
services/f3/driver/reactions.go
Normal file
59
services/f3/driver/reactions.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reactions struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reactions) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
reactionable := f3_tree.GetReactionable(o.GetNode())
|
||||||
|
reactionableID := f3_tree.GetReactionableID(o.GetNode())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
cond := builder.NewCond()
|
||||||
|
switch reactionable.GetKind() {
|
||||||
|
case f3_tree.KindIssue, f3_tree.KindPullRequest:
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, reactionableID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v %w", reactionableID, err))
|
||||||
|
}
|
||||||
|
cond = cond.And(builder.Eq{"reaction.issue_id": issue.ID})
|
||||||
|
case f3_tree.KindComment:
|
||||||
|
cond = cond.And(builder.Eq{"reaction.comment_id": reactionableID})
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected type %v", reactionable.GetKind()))
|
||||||
|
}
|
||||||
|
|
||||||
|
sess = sess.Where(cond)
|
||||||
|
if page > 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
}
|
||||||
|
reactions := make([]*issues_model.Reaction, 0, 10)
|
||||||
|
if err := sess.Find(&reactions); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing reactions: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(reactions...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReactions() generic.NodeDriverInterface {
|
||||||
|
return &reactions{}
|
||||||
|
}
|
161
services/f3/driver/release.go
Normal file
161
services/f3/driver/release.go
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &release{}
|
||||||
|
|
||||||
|
type release struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoRelease *repo_model.Release
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) SetNative(release any) {
|
||||||
|
o.forgejoRelease = release.(*repo_model.Release)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoRelease.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoRelease == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.Release{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoRelease.ID)),
|
||||||
|
TagName: o.forgejoRelease.TagName,
|
||||||
|
TargetCommitish: o.forgejoRelease.Target,
|
||||||
|
Name: o.forgejoRelease.Title,
|
||||||
|
Body: o.forgejoRelease.Note,
|
||||||
|
Draft: o.forgejoRelease.IsDraft,
|
||||||
|
Prerelease: o.forgejoRelease.IsPrerelease,
|
||||||
|
PublisherID: f3_tree.NewUserReference(o.forgejoRelease.Publisher.ID),
|
||||||
|
Created: o.forgejoRelease.CreatedUnix.AsTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) FromFormat(content f3.Interface) {
|
||||||
|
release := content.(*f3.Release)
|
||||||
|
|
||||||
|
o.forgejoRelease = &repo_model.Release{
|
||||||
|
ID: f3_util.ParseInt(release.GetID()),
|
||||||
|
PublisherID: release.PublisherID.GetIDAsInt(),
|
||||||
|
Publisher: &user_model.User{
|
||||||
|
ID: release.PublisherID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
TagName: release.TagName,
|
||||||
|
LowerTagName: strings.ToLower(release.TagName),
|
||||||
|
Target: release.TargetCommitish,
|
||||||
|
Title: release.Name,
|
||||||
|
Note: release.Body,
|
||||||
|
IsDraft: release.Draft,
|
||||||
|
IsPrerelease: release.Prerelease,
|
||||||
|
IsTag: false,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(release.Created.Unix()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
release, err := repo_model.GetReleaseByID(ctx, id)
|
||||||
|
if repo_model.IsErrReleaseNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("release %v %w", id, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
release.Publisher, err = user_model.GetUserByID(ctx, release.PublisherID)
|
||||||
|
if err != nil {
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
release.Publisher = user_model.NewGhostUser()
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoRelease = release
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoRelease.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoRelease.ID).Cols("title", "note").Update(o.forgejoRelease); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateReleaseCols: %v %v", o.forgejoRelease, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
o.forgejoRelease.RepoID = f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
owner := f3_tree.GetOwnerName(o.GetNode())
|
||||||
|
project := f3_tree.GetProjectName(o.GetNode())
|
||||||
|
repoPath := repo_model.RepoPath(owner, project)
|
||||||
|
gitRepo, err := git.OpenRepository(ctx, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
if err := release_service.CreateRelease(gitRepo, o.forgejoRelease, nil, ""); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("release created %d", o.forgejoRelease.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoRelease.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *release) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
repo, err := repo_model.GetRepositoryByID(ctx, project)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doer, err := user_model.GetAdminUser(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetAdminUser %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := release_service.DeleteReleaseByID(ctx, repo, o.forgejoRelease, doer, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRelease() generic.NodeDriverInterface {
|
||||||
|
return &release{}
|
||||||
|
}
|
42
services/f3/driver/releases.go
Normal file
42
services/f3/driver/releases.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type releases struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *releases) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
|
||||||
|
forgejoReleases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
|
||||||
|
ListOptions: db.ListOptions{Page: page, PageSize: pageSize},
|
||||||
|
IncludeDrafts: true,
|
||||||
|
IncludeTags: false,
|
||||||
|
RepoID: project,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing releases: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoReleases...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReleases() generic.NodeDriverInterface {
|
||||||
|
return &releases{}
|
||||||
|
}
|
36
services/f3/driver/repositories.go
Normal file
36
services/f3/driver/repositories.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repositories struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repositories) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
children := generic.NewChildrenSlice(0)
|
||||||
|
if page > 1 {
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
names := []string{f3.RepositoryNameDefault}
|
||||||
|
project := f3_tree.GetProject(o.GetNode()).ToFormat().(*f3.Project)
|
||||||
|
if project.HasWiki {
|
||||||
|
names = append(names, f3.RepositoryNameWiki)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(names...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRepositories() generic.NodeDriverInterface {
|
||||||
|
return &repositories{}
|
||||||
|
}
|
101
services/f3/driver/repository.go
Normal file
101
services/f3/driver/repository.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
helpers_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/repository"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &repository{}
|
||||||
|
|
||||||
|
type repository struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
name string
|
||||||
|
h helpers_repository.Interface
|
||||||
|
|
||||||
|
f *f3.Repository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) SetNative(repository any) {
|
||||||
|
o.name = repository.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) GetNativeID() string {
|
||||||
|
return o.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) NewFormat() f3.Interface {
|
||||||
|
return &f3.Repository{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) ToFormat() f3.Interface {
|
||||||
|
return &f3.Repository{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
Name: o.GetNativeID(),
|
||||||
|
FetchFunc: o.f.FetchFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) FromFormat(content f3.Interface) {
|
||||||
|
f := content.Clone().(*f3.Repository)
|
||||||
|
o.f = f
|
||||||
|
o.f.SetID(f.Name)
|
||||||
|
o.name = f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) Get(ctx context.Context) bool {
|
||||||
|
return o.h.Get(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) Put(ctx context.Context) generic.NodeID {
|
||||||
|
return o.upsert(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) Patch(ctx context.Context) {
|
||||||
|
o.upsert(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) upsert(ctx context.Context) generic.NodeID {
|
||||||
|
o.Trace("%s", o.GetNativeID())
|
||||||
|
o.h.Upsert(ctx, o.f)
|
||||||
|
return generic.NodeID(o.f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination string)) {
|
||||||
|
o.f.FetchFunc = fetchFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) getURL() string {
|
||||||
|
owner := f3_tree.GetOwnerName(o.GetNode())
|
||||||
|
repoName := f3_tree.GetProjectName(o.GetNode())
|
||||||
|
if o.f.GetID() == f3.RepositoryNameWiki {
|
||||||
|
repoName += ".wiki"
|
||||||
|
}
|
||||||
|
return repo_model.RepoPath(owner, repoName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) GetRepositoryURL() string {
|
||||||
|
return o.getURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *repository) GetRepositoryPushURL() string {
|
||||||
|
return o.getURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRepository(ctx context.Context) generic.NodeDriverInterface {
|
||||||
|
r := &repository{
|
||||||
|
f: &f3.Repository{},
|
||||||
|
}
|
||||||
|
r.h = helpers_repository.NewHelper(r)
|
||||||
|
return r
|
||||||
|
}
|
179
services/f3/driver/review.go
Normal file
179
services/f3/driver/review.go
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &review{}
|
||||||
|
|
||||||
|
type review struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoReview *issues_model.Review
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) SetNative(review any) {
|
||||||
|
o.forgejoReview = review.(*issues_model.Review)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoReview.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoReview == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
review := &f3.Review{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
ReviewerID: f3_tree.NewUserReference(o.forgejoReview.ReviewerID),
|
||||||
|
Official: o.forgejoReview.Official,
|
||||||
|
CommitID: o.forgejoReview.CommitID,
|
||||||
|
Content: o.forgejoReview.Content,
|
||||||
|
CreatedAt: o.forgejoReview.CreatedUnix.AsTime(),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch o.forgejoReview.Type {
|
||||||
|
case issues_model.ReviewTypeApprove:
|
||||||
|
review.State = f3.ReviewStateApproved
|
||||||
|
case issues_model.ReviewTypeReject:
|
||||||
|
review.State = f3.ReviewStateChangesRequested
|
||||||
|
case issues_model.ReviewTypeComment:
|
||||||
|
review.State = f3.ReviewStateCommented
|
||||||
|
case issues_model.ReviewTypePending:
|
||||||
|
review.State = f3.ReviewStatePending
|
||||||
|
case issues_model.ReviewTypeRequest:
|
||||||
|
review.State = f3.ReviewStateRequestReview
|
||||||
|
default:
|
||||||
|
review.State = f3.ReviewStateUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.forgejoReview.Reviewer != nil {
|
||||||
|
review.ReviewerID = f3_tree.NewUserReference(o.forgejoReview.Reviewer.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return review
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) FromFormat(content f3.Interface) {
|
||||||
|
review := content.(*f3.Review)
|
||||||
|
|
||||||
|
o.forgejoReview = &issues_model.Review{
|
||||||
|
ID: f3_util.ParseInt(review.GetID()),
|
||||||
|
ReviewerID: review.ReviewerID.GetIDAsInt(),
|
||||||
|
Reviewer: &user_model.User{
|
||||||
|
ID: review.ReviewerID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
Official: review.Official,
|
||||||
|
CommitID: review.CommitID,
|
||||||
|
Content: review.Content,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(review.CreatedAt.Unix()),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch review.State {
|
||||||
|
case f3.ReviewStateApproved:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypeApprove
|
||||||
|
case f3.ReviewStateChangesRequested:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypeReject
|
||||||
|
case f3.ReviewStateCommented:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypeComment
|
||||||
|
case f3.ReviewStatePending:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypePending
|
||||||
|
case f3.ReviewStateRequestReview:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypeRequest
|
||||||
|
default:
|
||||||
|
o.forgejoReview.Type = issues_model.ReviewTypeUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
review, err := issues_model.GetReviewByID(ctx, id)
|
||||||
|
if issues_model.IsErrReviewNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("review %v %w", id, err))
|
||||||
|
}
|
||||||
|
if err := review.LoadReviewer(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadReviewer %v %w", *review, err))
|
||||||
|
}
|
||||||
|
o.forgejoReview = review
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoReview.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoReview.ID).Cols("content").Update(o.forgejoReview); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateReviewCols: %v %v", o.forgejoReview, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
pullRequest := f3_tree.GetPullRequestID(o.GetNode())
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, pullRequest)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v", err))
|
||||||
|
}
|
||||||
|
o.forgejoReview.IssueID = issue.ID
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err := sess.NoAutoTime().Insert(o.forgejoReview); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("review created %d", o.forgejoReview.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoReview.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *review) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
pullRequest := f3_tree.GetPullRequestID(o.GetNode())
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, pullRequest)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v", err))
|
||||||
|
}
|
||||||
|
o.forgejoReview.IssueID = issue.ID
|
||||||
|
|
||||||
|
if err := issues_model.DeleteReview(ctx, o.forgejoReview); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReview() generic.NodeDriverInterface {
|
||||||
|
return &review{}
|
||||||
|
}
|
142
services/f3/driver/reviewcomment.go
Normal file
142
services/f3/driver/reviewcomment.go
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &reviewComment{}
|
||||||
|
|
||||||
|
type reviewComment struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoReviewComment *issues_model.Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) SetNative(reviewComment any) {
|
||||||
|
o.forgejoReviewComment = reviewComment.(*issues_model.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoReviewComment.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func patch2diff(patch string) string {
|
||||||
|
split := strings.Split(patch, "\n@@")
|
||||||
|
if len(split) == 2 {
|
||||||
|
return "@@" + split[1]
|
||||||
|
}
|
||||||
|
return patch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoReviewComment == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &f3.ReviewComment{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
PosterID: f3_tree.NewUserReference(o.forgejoReviewComment.Poster.ID),
|
||||||
|
Content: o.forgejoReviewComment.Content,
|
||||||
|
TreePath: o.forgejoReviewComment.TreePath,
|
||||||
|
DiffHunk: patch2diff(o.forgejoReviewComment.PatchQuoted),
|
||||||
|
Line: int(o.forgejoReviewComment.Line),
|
||||||
|
CommitID: o.forgejoReviewComment.CommitSHA,
|
||||||
|
CreatedAt: o.forgejoReviewComment.CreatedUnix.AsTime(),
|
||||||
|
UpdatedAt: o.forgejoReviewComment.UpdatedUnix.AsTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) FromFormat(content f3.Interface) {
|
||||||
|
reviewComment := content.(*f3.ReviewComment)
|
||||||
|
o.forgejoReviewComment = &issues_model.Comment{
|
||||||
|
ID: f3_util.ParseInt(reviewComment.GetID()),
|
||||||
|
PosterID: reviewComment.PosterID.GetIDAsInt(),
|
||||||
|
Poster: &user_model.User{
|
||||||
|
ID: reviewComment.PosterID.GetIDAsInt(),
|
||||||
|
},
|
||||||
|
TreePath: reviewComment.TreePath,
|
||||||
|
Content: reviewComment.Content,
|
||||||
|
// a hunk misses the patch header but it is never used so do not bother
|
||||||
|
// reconstructing it
|
||||||
|
Patch: reviewComment.DiffHunk,
|
||||||
|
PatchQuoted: reviewComment.DiffHunk,
|
||||||
|
Line: int64(reviewComment.Line),
|
||||||
|
CommitSHA: reviewComment.CommitID,
|
||||||
|
CreatedUnix: timeutil.TimeStamp(reviewComment.CreatedAt.Unix()),
|
||||||
|
UpdatedUnix: timeutil.TimeStamp(reviewComment.UpdatedAt.Unix()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
reviewComment, err := issues_model.GetCommentByID(ctx, id)
|
||||||
|
if issues_model.IsErrCommentNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("reviewComment %v %w", id, err))
|
||||||
|
}
|
||||||
|
if err := reviewComment.LoadPoster(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("LoadPoster %v %w", *reviewComment, err))
|
||||||
|
}
|
||||||
|
o.forgejoReviewComment = reviewComment
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoReviewComment.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoReviewComment.ID).Cols("content").Update(o.forgejoReviewComment); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateReviewCommentCols: %v %v", o.forgejoReviewComment, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) Put(ctx context.Context) generic.NodeID {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err := sess.NoAutoTime().Insert(o.forgejoReviewComment); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("reviewComment created %d", o.forgejoReviewComment.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoReviewComment.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComment) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
if err := issues_model.DeleteComment(ctx, o.forgejoReviewComment); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReviewComment() generic.NodeDriverInterface {
|
||||||
|
return &reviewComment{}
|
||||||
|
}
|
43
services/f3/driver/reviewcomments.go
Normal file
43
services/f3/driver/reviewcomments.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reviewComments struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviewComments) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
id := f3_tree.GetReviewID(o.GetNode())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx).
|
||||||
|
Table("comment").
|
||||||
|
Where("`review_id` = ? AND `type` = ?", id, issues_model.CommentTypeCode)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
}
|
||||||
|
forgejoReviewComments := make([]*issues_model.Comment, 0, pageSize)
|
||||||
|
if err := sess.Find(&forgejoReviewComments); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing reviewComments: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoReviewComments...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReviewComments() generic.NodeDriverInterface {
|
||||||
|
return &reviewComments{}
|
||||||
|
}
|
49
services/f3/driver/reviews.go
Normal file
49
services/f3/driver/reviews.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reviews struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *reviews) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
project := f3_tree.GetProjectID(o.GetNode())
|
||||||
|
pullRequest := f3_tree.GetPullRequestID(o.GetNode())
|
||||||
|
|
||||||
|
issue, err := issues_model.GetIssueByIndex(ctx, project, pullRequest)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetIssueByIndex %v %w", pullRequest, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx).
|
||||||
|
Table("review").
|
||||||
|
Where("`issue_id` = ?", issue.ID)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
}
|
||||||
|
forgejoReviews := make([]*issues_model.Review, 0, pageSize)
|
||||||
|
if err := sess.Find(&forgejoReviews); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing reviews: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(forgejoReviews...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newReviews() generic.NodeDriverInterface {
|
||||||
|
return &reviews{}
|
||||||
|
}
|
41
services/f3/driver/root.go
Normal file
41
services/f3/driver/root.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type root struct {
|
||||||
|
generic.NullDriver
|
||||||
|
|
||||||
|
content f3.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRoot(content f3.Interface) generic.NodeDriverInterface {
|
||||||
|
return &root{
|
||||||
|
content: content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *root) FromFormat(content f3.Interface) {
|
||||||
|
o.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *root) ToFormat() f3.Interface {
|
||||||
|
return o.content
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *root) Get(context.Context) bool { return true }
|
||||||
|
|
||||||
|
func (o *root) Put(context.Context) generic.NodeID {
|
||||||
|
return generic.NilID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *root) Patch(context.Context) {
|
||||||
|
}
|
15
services/f3/driver/tests/init.go
Normal file
15
services/f3/driver/tests/init.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tests_forge.RegisterFactory(driver_options.Name, newForgeTest)
|
||||||
|
}
|
39
services/f3/driver/tests/new.go
Normal file
39
services/f3/driver/tests/new.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
forge_test "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||||
|
)
|
||||||
|
|
||||||
|
type forgeTest struct {
|
||||||
|
forge_test.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forgeTest) NewOptions(t *testing.T) options.Interface {
|
||||||
|
return newTestOptions(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forgeTest) GetExceptions() []generic.Kind {
|
||||||
|
return []generic.Kind{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *forgeTest) GetNonTestUsers() []string {
|
||||||
|
return []string{
|
||||||
|
"user1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newForgeTest() forge_test.Interface {
|
||||||
|
t := &forgeTest{}
|
||||||
|
t.SetName(driver_options.Name)
|
||||||
|
return t
|
||||||
|
}
|
21
services/f3/driver/tests/options.go
Normal file
21
services/f3/driver/tests/options.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
forgejo_log "code.gitea.io/gitea/modules/log"
|
||||||
|
driver_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
"code.gitea.io/gitea/services/f3/util"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestOptions(t *testing.T) options.Interface {
|
||||||
|
o := options.GetFactory(driver_options.Name)().(*driver_options.Options)
|
||||||
|
o.SetLogger(util.NewF3Logger(nil, forgejo_log.GetLogger(forgejo_log.DEFAULT)))
|
||||||
|
return o
|
||||||
|
}
|
111
services/f3/driver/topic.go
Normal file
111
services/f3/driver/topic.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &topic{}
|
||||||
|
|
||||||
|
type topic struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoTopic *repo_model.Topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) SetNative(topic any) {
|
||||||
|
o.forgejoTopic = topic.(*repo_model.Topic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoTopic.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoTopic == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &f3.Topic{
|
||||||
|
Common: f3.NewCommon(o.GetNativeID()),
|
||||||
|
Name: o.forgejoTopic.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) FromFormat(content f3.Interface) {
|
||||||
|
topic := content.(*f3.Topic)
|
||||||
|
o.forgejoTopic = &repo_model.Topic{
|
||||||
|
ID: f3_util.ParseInt(topic.GetID()),
|
||||||
|
Name: topic.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
|
||||||
|
if has, err := db.GetEngine(ctx).Where("ID = ?", id).Get(o.forgejoTopic); err != nil {
|
||||||
|
panic(fmt.Errorf("topic %v %w", id, err))
|
||||||
|
} else if !has {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) Patch(ctx context.Context) {
|
||||||
|
o.Trace("%d", o.forgejoTopic.ID)
|
||||||
|
if _, err := db.GetEngine(ctx).ID(o.forgejoTopic.ID).Cols("name").Update(o.forgejoTopic); err != nil {
|
||||||
|
panic(fmt.Errorf("UpdateTopicCols: %v %v", o.forgejoTopic, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) Put(ctx context.Context) generic.NodeID {
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err := sess.Insert(o.forgejoTopic); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
o.Trace("topic created %d", o.forgejoTopic.ID)
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoTopic.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topic) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
|
||||||
|
if _, err := sess.Delete(&repo_model.RepoTopic{
|
||||||
|
TopicID: o.forgejoTopic.ID,
|
||||||
|
}); err != nil {
|
||||||
|
panic(fmt.Errorf("Delete RepoTopic for %v %v", o.forgejoTopic, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Delete(o.forgejoTopic); err != nil {
|
||||||
|
panic(fmt.Errorf("Delete Topic %v %v", o.forgejoTopic, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTopic() generic.NodeDriverInterface {
|
||||||
|
return &topic{}
|
||||||
|
}
|
41
services/f3/driver/topics.go
Normal file
41
services/f3/driver/topics.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type topics struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *topics) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
pageSize := o.getPageSize()
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: pageSize})
|
||||||
|
}
|
||||||
|
sess = sess.Select("`topic`.*")
|
||||||
|
topics := make([]*repo_model.Topic, 0, pageSize)
|
||||||
|
|
||||||
|
if err := sess.Find(&topics); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing topics: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(topics...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTopics() generic.NodeDriverInterface {
|
||||||
|
return &topics{}
|
||||||
|
}
|
104
services/f3/driver/tree.go
Normal file
104
services/f3/driver/tree.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
forgejo_options "code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type treeDriver struct {
|
||||||
|
generic.NullTreeDriver
|
||||||
|
|
||||||
|
options *forgejo_options.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *treeDriver) Init() {
|
||||||
|
o.NullTreeDriver.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *treeDriver) Factory(ctx context.Context, kind generic.Kind) generic.NodeDriverInterface {
|
||||||
|
switch kind {
|
||||||
|
case f3_tree.KindForge:
|
||||||
|
return newForge()
|
||||||
|
case f3_tree.KindOrganizations:
|
||||||
|
return newOrganizations()
|
||||||
|
case f3_tree.KindOrganization:
|
||||||
|
return newOrganization()
|
||||||
|
case f3_tree.KindUsers:
|
||||||
|
return newUsers()
|
||||||
|
case f3_tree.KindUser:
|
||||||
|
return newUser()
|
||||||
|
case f3_tree.KindProjects:
|
||||||
|
return newProjects()
|
||||||
|
case f3_tree.KindProject:
|
||||||
|
return newProject()
|
||||||
|
case f3_tree.KindIssues:
|
||||||
|
return newIssues()
|
||||||
|
case f3_tree.KindIssue:
|
||||||
|
return newIssue()
|
||||||
|
case f3_tree.KindComments:
|
||||||
|
return newComments()
|
||||||
|
case f3_tree.KindComment:
|
||||||
|
return newComment()
|
||||||
|
case f3_tree.KindAssets:
|
||||||
|
return newAssets()
|
||||||
|
case f3_tree.KindAsset:
|
||||||
|
return newAsset()
|
||||||
|
case f3_tree.KindLabels:
|
||||||
|
return newLabels()
|
||||||
|
case f3_tree.KindLabel:
|
||||||
|
return newLabel()
|
||||||
|
case f3_tree.KindReactions:
|
||||||
|
return newReactions()
|
||||||
|
case f3_tree.KindReaction:
|
||||||
|
return newReaction()
|
||||||
|
case f3_tree.KindReviews:
|
||||||
|
return newReviews()
|
||||||
|
case f3_tree.KindReview:
|
||||||
|
return newReview()
|
||||||
|
case f3_tree.KindReviewComments:
|
||||||
|
return newReviewComments()
|
||||||
|
case f3_tree.KindReviewComment:
|
||||||
|
return newReviewComment()
|
||||||
|
case f3_tree.KindMilestones:
|
||||||
|
return newMilestones()
|
||||||
|
case f3_tree.KindMilestone:
|
||||||
|
return newMilestone()
|
||||||
|
case f3_tree.KindPullRequests:
|
||||||
|
return newPullRequests()
|
||||||
|
case f3_tree.KindPullRequest:
|
||||||
|
return newPullRequest()
|
||||||
|
case f3_tree.KindReleases:
|
||||||
|
return newReleases()
|
||||||
|
case f3_tree.KindRelease:
|
||||||
|
return newRelease()
|
||||||
|
case f3_tree.KindTopics:
|
||||||
|
return newTopics()
|
||||||
|
case f3_tree.KindTopic:
|
||||||
|
return newTopic()
|
||||||
|
case f3_tree.KindRepositories:
|
||||||
|
return newRepositories()
|
||||||
|
case f3_tree.KindRepository:
|
||||||
|
return newRepository(ctx)
|
||||||
|
case generic.KindRoot:
|
||||||
|
return newRoot(o.GetTree().(f3_tree.TreeInterface).NewFormat(kind))
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected kind %s", kind))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTreeDriver(tree generic.TreeInterface, anyOptions any) generic.TreeDriverInterface {
|
||||||
|
driver := &treeDriver{
|
||||||
|
options: anyOptions.(*forgejo_options.Options),
|
||||||
|
}
|
||||||
|
driver.Init()
|
||||||
|
return driver
|
||||||
|
}
|
128
services/f3/driver/user.go
Normal file
128
services/f3/driver/user.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/f3"
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_util "code.forgejo.org/f3/gof3/v3/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ f3_tree.ForgeDriverInterface = &user{}
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
common
|
||||||
|
|
||||||
|
forgejoUser *user_model.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemUserByName(name string) *user_model.User {
|
||||||
|
switch name {
|
||||||
|
case user_model.GhostUserName:
|
||||||
|
return user_model.NewGhostUser()
|
||||||
|
case user_model.ActionsUserName:
|
||||||
|
return user_model.NewActionsUser()
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) SetNative(user any) {
|
||||||
|
o.forgejoUser = user.(*user_model.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) GetNativeID() string {
|
||||||
|
return fmt.Sprintf("%d", o.forgejoUser.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) NewFormat() f3.Interface {
|
||||||
|
node := o.GetNode()
|
||||||
|
return node.GetTree().(f3_tree.TreeInterface).NewFormat(node.GetKind())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) ToFormat() f3.Interface {
|
||||||
|
if o.forgejoUser == nil {
|
||||||
|
return o.NewFormat()
|
||||||
|
}
|
||||||
|
return &f3.User{
|
||||||
|
Common: f3.NewCommon(fmt.Sprintf("%d", o.forgejoUser.ID)),
|
||||||
|
UserName: o.forgejoUser.Name,
|
||||||
|
Name: o.forgejoUser.FullName,
|
||||||
|
Email: o.forgejoUser.Email,
|
||||||
|
IsAdmin: o.forgejoUser.IsAdmin,
|
||||||
|
Password: o.forgejoUser.Passwd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) FromFormat(content f3.Interface) {
|
||||||
|
user := content.(*f3.User)
|
||||||
|
o.forgejoUser = &user_model.User{
|
||||||
|
Type: user_model.UserTypeRemoteUser,
|
||||||
|
ID: f3_util.ParseInt(user.GetID()),
|
||||||
|
Name: user.UserName,
|
||||||
|
FullName: user.Name,
|
||||||
|
Email: user.Email,
|
||||||
|
IsAdmin: user.IsAdmin,
|
||||||
|
Passwd: user.Password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) Get(ctx context.Context) bool {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
id := f3_util.ParseInt(string(node.GetID()))
|
||||||
|
u, err := user_model.GetPossibleUserByID(ctx, id)
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("user %v %w", id, err))
|
||||||
|
}
|
||||||
|
o.forgejoUser = u
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) Patch(context.Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) Put(ctx context.Context) generic.NodeID {
|
||||||
|
if user := getSystemUserByName(o.forgejoUser.Name); user != nil {
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", user.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
o.forgejoUser.LowerName = strings.ToLower(o.forgejoUser.Name)
|
||||||
|
o.Trace("%v", *o.forgejoUser)
|
||||||
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
|
IsActive: optional.Some(true),
|
||||||
|
}
|
||||||
|
err := user_model.CreateUser(ctx, o.forgejoUser, overwriteDefault)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", o.forgejoUser.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *user) Delete(ctx context.Context) {
|
||||||
|
node := o.GetNode()
|
||||||
|
o.Trace("%s", node.GetID())
|
||||||
|
|
||||||
|
if err := user_service.DeleteUser(ctx, o.forgejoUser, true); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUser() generic.NodeDriverInterface {
|
||||||
|
return &user{}
|
||||||
|
}
|
48
services/f3/driver/users.go
Normal file
48
services/f3/driver/users.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||||
|
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type users struct {
|
||||||
|
container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *users) ListPage(ctx context.Context, page int) generic.ChildrenSlice {
|
||||||
|
sess := db.GetEngine(ctx).In("type", user_model.UserTypeIndividual, user_model.UserTypeRemoteUser)
|
||||||
|
if page != 0 {
|
||||||
|
sess = db.SetSessionPagination(sess, &db.ListOptions{Page: page, PageSize: o.getPageSize()})
|
||||||
|
}
|
||||||
|
sess = sess.Select("`user`.*")
|
||||||
|
users := make([]*user_model.User, 0, o.getPageSize())
|
||||||
|
|
||||||
|
if err := sess.Find(&users); err != nil {
|
||||||
|
panic(fmt.Errorf("error while listing users: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3_tree.ConvertListed(ctx, o.GetNode(), f3_tree.ConvertToAny(users...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *users) GetIDFromName(ctx context.Context, name string) generic.NodeID {
|
||||||
|
user, err := user_model.GetUserByName(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("GetUserByName: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return generic.NodeID(fmt.Sprintf("%d", user.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUsers() generic.NodeDriverInterface {
|
||||||
|
return &users{}
|
||||||
|
}
|
97
services/f3/util/logger.go
Normal file
97
services/f3/util/logger.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
forgejo_log "code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/migration"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type f3Logger struct {
|
||||||
|
m migration.Messenger
|
||||||
|
l forgejo_log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) Message(message string, args ...any) {
|
||||||
|
if o.m != nil {
|
||||||
|
o.m(message, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) SetLevel(level logger.Level) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func forgejoLevelToF3Level(level forgejo_log.Level) logger.Level {
|
||||||
|
switch level {
|
||||||
|
case forgejo_log.TRACE:
|
||||||
|
return logger.Trace
|
||||||
|
case forgejo_log.DEBUG:
|
||||||
|
return logger.Debug
|
||||||
|
case forgejo_log.INFO:
|
||||||
|
return logger.Info
|
||||||
|
case forgejo_log.WARN:
|
||||||
|
return logger.Warn
|
||||||
|
case forgejo_log.ERROR:
|
||||||
|
return logger.Error
|
||||||
|
case forgejo_log.FATAL:
|
||||||
|
return logger.Fatal
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected level %d", level))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func f3LevelToForgejoLevel(level logger.Level) forgejo_log.Level {
|
||||||
|
switch level {
|
||||||
|
case logger.Trace:
|
||||||
|
return forgejo_log.TRACE
|
||||||
|
case logger.Debug:
|
||||||
|
return forgejo_log.DEBUG
|
||||||
|
case logger.Info:
|
||||||
|
return forgejo_log.INFO
|
||||||
|
case logger.Warn:
|
||||||
|
return forgejo_log.WARN
|
||||||
|
case logger.Error:
|
||||||
|
return forgejo_log.ERROR
|
||||||
|
case logger.Fatal:
|
||||||
|
return forgejo_log.FATAL
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unexpected level %d", level))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) GetLevel() logger.Level {
|
||||||
|
return forgejoLevelToF3Level(o.l.GetLevel())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) Log(skip int, level logger.Level, format string, args ...any) {
|
||||||
|
o.l.Log(skip+1, f3LevelToForgejoLevel(level), format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) Trace(message string, args ...any) {
|
||||||
|
o.l.Log(1, forgejo_log.TRACE, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) Debug(message string, args ...any) {
|
||||||
|
o.l.Log(1, forgejo_log.DEBUG, message, args...)
|
||||||
|
}
|
||||||
|
func (o *f3Logger) Info(message string, args ...any) { o.l.Log(1, forgejo_log.INFO, message, args...) }
|
||||||
|
func (o *f3Logger) Warn(message string, args ...any) { o.l.Log(1, forgejo_log.WARN, message, args...) }
|
||||||
|
func (o *f3Logger) Error(message string, args ...any) {
|
||||||
|
o.l.Log(1, forgejo_log.ERROR, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *f3Logger) Fatal(message string, args ...any) {
|
||||||
|
o.l.Log(1, forgejo_log.FATAL, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewF3Logger(messenger migration.Messenger, logger forgejo_log.Logger) logger.Interface {
|
||||||
|
return &f3Logger{
|
||||||
|
m: messenger,
|
||||||
|
l: logger,
|
||||||
|
}
|
||||||
|
}
|
89
services/f3/util/logger_test.go
Normal file
89
services/f3/util/logger_test.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
forgejo_log "code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
|
"code.forgejo.org/f3/gof3/v3/logger"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestF3UtilMessage(t *testing.T) {
|
||||||
|
expected := "EXPECTED MESSAGE"
|
||||||
|
var actual string
|
||||||
|
logger := NewF3Logger(func(message string, args ...any) {
|
||||||
|
actual = fmt.Sprintf(message, args...)
|
||||||
|
}, nil)
|
||||||
|
logger.Message("EXPECTED %s", "MESSAGE")
|
||||||
|
assert.EqualValues(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestF3UtilLogger(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
level logger.Level
|
||||||
|
call func(logger.MessageInterface, string, ...any)
|
||||||
|
}{
|
||||||
|
{level: logger.Trace, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Trace(message, args...) }},
|
||||||
|
{level: logger.Debug, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Debug(message, args...) }},
|
||||||
|
{level: logger.Info, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Info(message, args...) }},
|
||||||
|
{level: logger.Warn, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Warn(message, args...) }},
|
||||||
|
{level: logger.Error, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Error(message, args...) }},
|
||||||
|
{level: logger.Fatal, call: func(logger logger.MessageInterface, message string, args ...any) { logger.Fatal(message, args...) }},
|
||||||
|
} {
|
||||||
|
t.Run(testCase.level.String(), func(t *testing.T) {
|
||||||
|
testLoggerCase(t, testCase.level, testCase.call)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoggerCase(t *testing.T, level logger.Level, loggerFunc func(logger.MessageInterface, string, ...any)) {
|
||||||
|
lc, cleanup := test.NewLogChecker(forgejo_log.DEFAULT, f3LevelToForgejoLevel(level))
|
||||||
|
defer cleanup()
|
||||||
|
stopMark := "STOP"
|
||||||
|
lc.StopMark(stopMark)
|
||||||
|
filtered := []string{
|
||||||
|
"MESSAGE HERE",
|
||||||
|
}
|
||||||
|
moreVerbose := logger.MoreVerbose(level)
|
||||||
|
if moreVerbose != nil {
|
||||||
|
filtered = append(filtered, "MESSAGE MORE VERBOSE")
|
||||||
|
}
|
||||||
|
lessVerbose := logger.LessVerbose(level)
|
||||||
|
if lessVerbose != nil {
|
||||||
|
filtered = append(filtered, "MESSAGE LESS VERBOSE")
|
||||||
|
}
|
||||||
|
lc.Filter(filtered...)
|
||||||
|
|
||||||
|
logger := NewF3Logger(nil, forgejo_log.GetLogger(forgejo_log.DEFAULT))
|
||||||
|
loggerFunc(logger, "MESSAGE %s", "HERE")
|
||||||
|
if moreVerbose != nil {
|
||||||
|
logger.Log(1, *moreVerbose, "MESSAGE %s", "MORE VERBOSE")
|
||||||
|
}
|
||||||
|
if lessVerbose != nil {
|
||||||
|
logger.Log(1, *lessVerbose, "MESSAGE %s", "LESS VERBOSE")
|
||||||
|
}
|
||||||
|
logger.Fatal(stopMark)
|
||||||
|
|
||||||
|
logFiltered, logStopped := lc.Check(5 * time.Second)
|
||||||
|
assert.True(t, logStopped)
|
||||||
|
i := 0
|
||||||
|
assert.True(t, logFiltered[i], filtered[i])
|
||||||
|
if moreVerbose != nil {
|
||||||
|
i++
|
||||||
|
require.True(t, len(logFiltered) > i)
|
||||||
|
assert.False(t, logFiltered[i], filtered[i])
|
||||||
|
}
|
||||||
|
if lessVerbose != nil {
|
||||||
|
i++
|
||||||
|
require.True(t, len(logFiltered) > i)
|
||||||
|
assert.True(t, logFiltered[i], filtered[i])
|
||||||
|
}
|
||||||
|
}
|
135
tests/integration/cmd_forgejo_f3_test.go
Normal file
135
tests/integration/cmd_forgejo_f3_test.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||||||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/cmd/forgejo"
|
||||||
|
"code.gitea.io/gitea/services/f3/driver/options"
|
||||||
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
_ "code.gitea.io/gitea/services/f3/driver"
|
||||||
|
_ "code.gitea.io/gitea/services/f3/driver/tests"
|
||||||
|
|
||||||
|
f3_filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||||
|
f3_logger "code.forgejo.org/f3/gof3/v3/logger"
|
||||||
|
f3_options "code.forgejo.org/f3/gof3/v3/options"
|
||||||
|
f3_generic "code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||||
|
f3_tests "code.forgejo.org/f3/gof3/v3/tree/tests/f3"
|
||||||
|
f3_tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runApp(ctx context.Context, args ...string) (string, error) {
|
||||||
|
l := f3_logger.NewCaptureLogger()
|
||||||
|
ctx = f3_logger.ContextSetLogger(ctx, l)
|
||||||
|
ctx = forgejo.ContextSetNoInit(ctx, true)
|
||||||
|
|
||||||
|
app := cli.NewApp()
|
||||||
|
|
||||||
|
app.Writer = l.GetBuffer()
|
||||||
|
app.ErrWriter = l.GetBuffer()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Println(l.String())
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
app.Commands = []*cli.Command{
|
||||||
|
forgejo.SubcmdF3Mirror(ctx),
|
||||||
|
}
|
||||||
|
err := app.Run(args)
|
||||||
|
|
||||||
|
fmt.Println(l.String())
|
||||||
|
|
||||||
|
return l.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestF3_CmdMirror_LocalForgejo(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
mirrorOptions := f3_tests_forge.GetFactory(options.Name)().NewOptions(t)
|
||||||
|
mirrorTree := f3_generic.GetFactory("f3")(ctx, mirrorOptions)
|
||||||
|
|
||||||
|
fixtureOptions := f3_tests_forge.GetFactory(f3_filesystem_options.Name)().NewOptions(t)
|
||||||
|
fixtureTree := f3_generic.GetFactory("f3")(ctx, fixtureOptions)
|
||||||
|
|
||||||
|
log := fixtureTree.GetLogger()
|
||||||
|
creator := f3_tests.NewCreator(t, log)
|
||||||
|
|
||||||
|
log.Trace("======= build fixture")
|
||||||
|
|
||||||
|
var fromPath string
|
||||||
|
{
|
||||||
|
fixtureUserID := "userID01"
|
||||||
|
fixtureProjectID := "projectID01"
|
||||||
|
|
||||||
|
userFormat := creator.GenerateUser()
|
||||||
|
userFormat.SetID(fixtureUserID)
|
||||||
|
users := fixtureTree.MustFind(f3_generic.NewPathFromString("/forge/users"))
|
||||||
|
user := users.CreateChild(ctx)
|
||||||
|
user.FromFormat(userFormat)
|
||||||
|
user.Upsert(ctx)
|
||||||
|
require.EqualValues(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName))
|
||||||
|
|
||||||
|
projectFormat := creator.GenerateProject()
|
||||||
|
projectFormat.SetID(fixtureProjectID)
|
||||||
|
projects := user.MustFind(f3_generic.NewPathFromString("projects"))
|
||||||
|
project := projects.CreateChild(ctx)
|
||||||
|
project.FromFormat(projectFormat)
|
||||||
|
project.Upsert(ctx)
|
||||||
|
require.EqualValues(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name))
|
||||||
|
|
||||||
|
fromPath = fmt.Sprintf("/forge/users/%s/projects/%s", userFormat.UserName, projectFormat.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("======= create mirror")
|
||||||
|
|
||||||
|
var toPath string
|
||||||
|
var projects f3_generic.NodeInterface
|
||||||
|
{
|
||||||
|
userFormat := creator.GenerateUser()
|
||||||
|
users := mirrorTree.MustFind(f3_generic.NewPathFromString("/forge/users"))
|
||||||
|
user := users.CreateChild(ctx)
|
||||||
|
user.FromFormat(userFormat)
|
||||||
|
user.Upsert(ctx)
|
||||||
|
require.EqualValues(t, user.GetID(), users.GetIDFromName(ctx, userFormat.UserName))
|
||||||
|
|
||||||
|
projectFormat := creator.GenerateProject()
|
||||||
|
projects = user.MustFind(f3_generic.NewPathFromString("projects"))
|
||||||
|
project := projects.CreateChild(ctx)
|
||||||
|
project.FromFormat(projectFormat)
|
||||||
|
project.Upsert(ctx)
|
||||||
|
require.EqualValues(t, project.GetID(), projects.GetIDFromName(ctx, projectFormat.Name))
|
||||||
|
|
||||||
|
toPath = fmt.Sprintf("/forge/users/%s/projects/%s", userFormat.UserName, projectFormat.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace("======= mirror %s => %s", fromPath, toPath)
|
||||||
|
output, err := runApp(ctx,
|
||||||
|
"f3", "mirror",
|
||||||
|
"--from-type", f3_filesystem_options.Name,
|
||||||
|
"--from-path", fromPath,
|
||||||
|
"--from-filesystem-directory", fixtureOptions.(f3_options.URLInterface).GetURL(),
|
||||||
|
|
||||||
|
"--to-type", options.Name,
|
||||||
|
"--to-path", toPath,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
log.Trace("======= assert")
|
||||||
|
require.Contains(t, output, fmt.Sprintf("mirror %s", fromPath))
|
||||||
|
projects.List(ctx)
|
||||||
|
require.NotEmpty(t, projects.GetChildren())
|
||||||
|
log.Trace("======= project %s", projects.GetChildren()[0])
|
||||||
|
}
|
Loading…
Reference in a new issue