// Copyright Earl Warren // Copyright Loïc Dachary // SPDX-License-Identifier: MIT package gitlab import ( "context" "encoding/json" "fmt" "io" "net/http" gitlab_options "code.forgejo.org/f3/gof3/v3/forges/gitlab/options" "code.forgejo.org/f3/gof3/v3/kind" f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3" "code.forgejo.org/f3/gof3/v3/tree/generic" "github.com/hashicorp/go-version" "gitlab.com/gitlab-org/api/client-go" ) type treeDriver struct { generic.NullTreeDriver user *gitlab.User client *gitlab.Client options *gitlab_options.Options version *version.Version } func (o *treeDriver) GetClient() *gitlab.Client { return o.client } func (o *treeDriver) GetIsAdmin() bool { return o.user.IsAdmin } func (o *treeDriver) SetIsAdmin() { user, _, err := o.client.Users.CurrentUser() if err != nil { panic(fmt.Errorf("Failed to get information about the user for: %s. Error: %v", o.options.GetURL(), err)) } o.user = user o.options.SetUsername(user.Username) } var ( ForgejoVersion700 = version.Must(version.NewVersion("7.0.0")) // 1.22 ForgejoVersion600 = version.Must(version.NewVersion("6.0.0")) // 1.21 ForgejoVersion500 = version.Must(version.NewVersion("5.0.0")) // 1.20.1 ForgejoVersion501 = version.Must(version.NewVersion("5.0.1")) // 1.20.2 ForgejoVersion502 = version.Must(version.NewVersion("5.0.2")) // 1.20.3 ForgejoVersion503 = version.Must(version.NewVersion("5.0.3")) // 1.20.4 ForgejoVersion504 = version.Must(version.NewVersion("5.0.4")) // 1.20.5 ForgejoVersion4 = version.Must(version.NewVersion("4.0.0")) // 1.19 ForgejoVersionNotFound = version.Must(version.NewVersion("1.0.0")) ) func (o *treeDriver) GetVersion() *version.Version { o.SetVersion() return o.version } func (o *treeDriver) SetVersion() { if o.version != nil { return } client := &http.Client{} url := fmt.Sprintf("%s/api/forgejo/v1/version", o.options.GetURL()) req, err := http.NewRequest("GET", url, nil) if err != nil { panic(err) } resp, err := client.Do(req) if err != nil { panic(fmt.Errorf("while getting %s %w", url, err)) } switch resp.StatusCode { case http.StatusNotFound: o.version = ForgejoVersionNotFound case http.StatusOK: v := struct{ Version string }{} body, err := io.ReadAll(resp.Body) if err != nil { panic(fmt.Errorf("reading response body %+v %w", resp, err)) } if err := json.Unmarshal(body, &v); err != nil { panic(fmt.Errorf("decoding JSON response from %s %s %w", url, string(body), err)) } o.version = version.Must(version.NewVersion(v.Version)) default: panic(fmt.Errorf("unexpected status code fetching %s %d %v", url, resp.StatusCode, resp)) } } func (o *treeDriver) maybeSudo(uid interface{}) []gitlab.RequestOptionFunc { if !o.GetIsAdmin() { return []gitlab.RequestOptionFunc{} } if name, ok := uid.(string); ok && name == o.options.GetUsername() { return []gitlab.RequestOptionFunc{} } return []gitlab.RequestOptionFunc{ gitlab.WithSudo(uid), } } func (o *treeDriver) Init() { o.NullTreeDriver.Init() var err error var client *gitlab.Client if o.options.GetToken() != "" { o.Debug("Connecting to %s with token", o.options.GetURL()) client, err = gitlab.NewClient(o.options.GetToken(), gitlab.WithBaseURL(o.options.GetURL()), gitlab.WithHTTPClient(o.options.GetNewMigrationHTTPClient()())) } else { o.Debug("Connecting to %s as user %s", o.options.GetURL(), o.options.GetUsername()) client, err = gitlab.NewBasicAuthClient(o.options.GetUsername(), o.options.GetPassword(), gitlab.WithBaseURL(o.options.GetURL()), gitlab.WithHTTPClient(o.options.GetNewMigrationHTTPClient()())) } if err != nil { panic(fmt.Errorf("Failed to create Forgejo client for: %s. Error: %v", o.options.GetURL(), err)) } o.client = client o.SetIsAdmin() if o.GetIsAdmin() { o.Debug("Connected as admin") } else { o.Debug("Connected as regular user") } } func newTreeDriver(tree generic.TreeInterface, anyOptions any) generic.TreeDriverInterface { driver := &treeDriver{ options: anyOptions.(*gitlab_options.Options), } driver.SetTree(tree) driver.Init() return driver } func (o *treeDriver) Factory(ctx context.Context, k kind.Kind) generic.NodeDriverInterface { switch k { 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.KindRepositories: // return newRepositories() // case f3_tree.KindRepository: // return newRepository(ctx) case kind.KindRoot: return newRoot(o.GetTree().(f3_tree.TreeInterface).NewFormat(k)) default: panic(fmt.Errorf("unexpected kind %s", k)) } }