package app import ( "crypto/tls" "encoding/json" "errors" "fmt" "net/http" "net/url" "github.com/harrisoncramer/gitlab.nvim/cmd/app/git" "github.com/hashicorp/go-retryablehttp" gitlab "gitlab.com/gitlab-org/api/client-go" ) type ProjectInfo struct { ProjectId string MergeId int64 } /* The Client struct embeds all the methods from Gitlab for the different services */ type Client struct { gitlab.MergeRequestsServiceInterface gitlab.MergeRequestApprovalsServiceInterface gitlab.DiscussionsServiceInterface gitlab.ProjectsServiceInterface gitlab.ProjectMembersServiceInterface gitlab.JobsServiceInterface gitlab.PipelinesServiceInterface gitlab.LabelsServiceInterface gitlab.AwardEmojiServiceInterface gitlab.UsersServiceInterface gitlab.DraftNotesServiceInterface gitlab.ProjectMarkdownUploadsServiceInterface } /* NewClient parses and validates the project settings and initializes the Gitlab client. */ func NewClient() (*Client, error) { if pluginOptions.GitlabUrl == "" { return nil, errors.New("GitLab instance URL cannot be empty") } var apiCustUrl = fmt.Sprintf("%s/api/v4", pluginOptions.GitlabUrl) gitlabOptions := []gitlab.ClientOptionFunc{ gitlab.WithBaseURL(apiCustUrl), } if pluginOptions.Debug.GitlabRequest { gitlabOptions = append(gitlabOptions, gitlab.WithRequestLogHook( func(l retryablehttp.Logger, r *http.Request, i int) { logRequest("REQUEST TO GITLAB", r) }, )) } if pluginOptions.Debug.GitlabResponse { gitlabOptions = append(gitlabOptions, gitlab.WithResponseLogHook(func(l retryablehttp.Logger, response *http.Response) { logResponse("RESPONSE FROM GITLAB", response) }, )) } tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: pluginOptions.ConnectionSettings.Insecure, }, } if proxy := pluginOptions.ConnectionSettings.Proxy; proxy != "" { u, err := url.Parse(proxy) if err != nil { return nil, fmt.Errorf("parse proxy url: %w", err) } tr.Proxy = http.ProxyURL(u) } retryClient := retryablehttp.NewClient() retryClient.HTTPClient.Transport = tr gitlabOptions = append(gitlabOptions, gitlab.WithHTTPClient(retryClient.HTTPClient)) gitlabOptions = append(gitlabOptions, gitlab.WithoutRetries()) client, err := gitlab.NewClient(pluginOptions.AuthToken, gitlabOptions...) if err != nil { return nil, fmt.Errorf("failed to create client: %v", err) } return &Client{ client.MergeRequests, client.MergeRequestApprovals, client.Discussions, client.Projects, client.ProjectMembers, client.Jobs, client.Pipelines, client.Labels, client.AwardEmoji, client.Users, client.DraftNotes, client.ProjectMarkdownUploads, }, nil } /* InitProjectSettings fetch the project ID using the client */ func InitProjectSettings(c *Client, gitInfo git.GitData) (*ProjectInfo, error) { opt := gitlab.GetProjectOptions{} project, _, err := c.GetProject(gitInfo.ProjectPath(), &opt) if err != nil { return nil, fmt.Errorf("error getting project at %s: %w", gitInfo.RemoteUrl, err) } if project == nil { return nil, fmt.Errorf("could not find project at %s", gitInfo.RemoteUrl) } projectId := fmt.Sprint(project.ID) return &ProjectInfo{ ProjectId: projectId, }, nil } /* handleError is a utililty handler that returns errors to the client along with their statuses and messages */ func handleError(w http.ResponseWriter, err error, message string, status int) { w.WriteHeader(status) response := ErrorResponse{ Message: message, Details: err.Error(), } err = json.NewEncoder(w).Encode(response) if err != nil { handleError(w, err, "Could not encode error response", http.StatusInternalServerError) } }