-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
includes refactor to split each provider into separate files
- Loading branch information
1 parent
fb5357d
commit 282dc8a
Showing
5 changed files
with
374 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package origin | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"html" | ||
"net/http" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
// AzureDevOpsProvider implements IssueProvider for Azure DevOps repositories. | ||
type AzureDevOpsProvider struct { | ||
BaseProvider | ||
org string // Azure DevOps organization | ||
} | ||
|
||
// NewAzureDevOpsProvider creates a new Azure DevOps provider with the given configuration. | ||
func NewAzureDevOpsProvider(config Config) *AzureDevOpsProvider { | ||
org := parseAzureDevOpsURL(config.URL) | ||
return &AzureDevOpsProvider{ | ||
BaseProvider: NewBaseProvider(config), | ||
org: org, | ||
} | ||
} | ||
|
||
// createRequest creates an Azure DevOps API request with appropriate headers. | ||
func (a *AzureDevOpsProvider) createRequest(issueNumber string) (*http.Request, error) { | ||
url := fmt.Sprintf( | ||
"https://dev.azure.com/%s/_apis/wit/workitems/%s?api-version=7.1", | ||
a.org, | ||
issueNumber, | ||
) | ||
|
||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create request: %w", err) | ||
} | ||
|
||
req.Header.Set("Content-Type", "application/json") | ||
encodedPat := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(":%s", a.config.Token))) | ||
if a.config.Token != "" { | ||
req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPat)) | ||
} | ||
|
||
return req, nil | ||
} | ||
|
||
// GetIssue fetches work item details from Azure DevOps. | ||
func (a *AzureDevOpsProvider) GetIssue(issueNumber string) (*Issue, error) { | ||
req, err := a.createRequest(issueNumber) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := a.doRequest(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp == nil { | ||
return nil, nil | ||
} | ||
defer resp.Body.Close() | ||
|
||
var azureResponse struct { | ||
ID int `json:"id"` | ||
Fields struct { | ||
Title string `json:"System.Title"` | ||
Description string `json:"System.Description"` | ||
} `json:"fields"` | ||
} | ||
|
||
if err := json.NewDecoder(resp.Body).Decode(&azureResponse); err != nil { | ||
return nil, fmt.Errorf("failed to decode response: %w", err) | ||
} | ||
|
||
return &Issue{ | ||
Number: azureResponse.ID, | ||
Title: azureResponse.Fields.Title, | ||
Body: cleanDescription(azureResponse.Fields.Description), | ||
}, nil | ||
} | ||
|
||
// parseAzureDevOpsURL extracts organization name from an Azure DevOps URL. | ||
func parseAzureDevOpsURL(url string) (org string) { | ||
url = strings.TrimSuffix(strings.TrimSuffix(url, "/"), ".git") | ||
parts := strings.Split(url, "/") | ||
for i, part := range parts { | ||
if part == "dev.azure.com" && i+1 < len(parts) { | ||
return parts[i+1] | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
// cleanDescription removes HTML tags and whitespace from the issue description. | ||
func cleanDescription(description string) string { | ||
description = html.UnescapeString(description) | ||
description = regexp.MustCompile(`<[^>]*>`).ReplaceAllString(description, "") | ||
return strings.TrimSpace(description) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package origin | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
// GitHubProvider implements IssueProvider for GitHub repositories. | ||
type GitHubProvider struct { | ||
BaseProvider | ||
owner string // GitHub repository owner | ||
repo string // GitHub repository name | ||
} | ||
|
||
// NewGitHubProvider creates a new GitHub provider with the given configuration. | ||
func NewGitHubProvider(config Config) *GitHubProvider { | ||
owner, repo := parseGitHubURL(config.URL) | ||
return &GitHubProvider{ | ||
BaseProvider: NewBaseProvider(config), | ||
owner: owner, | ||
repo: repo, | ||
} | ||
} | ||
|
||
// createRequest creates a GitHub API request with appropriate headers. | ||
func (g *GitHubProvider) createRequest(issueNumber string) (*http.Request, error) { | ||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/issues/%s", | ||
g.owner, g.repo, issueNumber) | ||
|
||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create request: %w", err) | ||
} | ||
|
||
req.Header.Set("Accept", "application/vnd.github.v3+json") | ||
req.Header.Set("User-Agent", "go-changelog") | ||
|
||
if g.config.Token != "" { | ||
req.Header.Set("Authorization", "Bearer "+g.config.Token) | ||
} | ||
|
||
return req, nil | ||
} | ||
|
||
// GetIssue fetches issue details from GitHub. | ||
func (g *GitHubProvider) GetIssue(issueNumber string) (*Issue, error) { | ||
req, err := g.createRequest(issueNumber) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := g.doRequest(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp == nil { | ||
return nil, nil | ||
} | ||
defer resp.Body.Close() | ||
|
||
var issue Issue | ||
if err := json.NewDecoder(resp.Body).Decode(&issue); err != nil { | ||
return nil, fmt.Errorf("failed to decode response: %w", err) | ||
} | ||
|
||
return &issue, nil | ||
} | ||
|
||
// parseGitHubURL extracts owner and repository name from a GitHub URL. | ||
func parseGitHubURL(url string) (owner, repo string) { | ||
url = strings.TrimSuffix(strings.TrimSuffix(url, "/"), ".git") | ||
parts := strings.Split(url, "/") | ||
for i, part := range parts { | ||
if part == "github.com" && i+2 < len(parts) { | ||
return parts[i+1], parts[i+2] | ||
} | ||
} | ||
return "", "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package origin | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
) | ||
|
||
// GitLabProvider implements IssueProvider for GitLab repositories | ||
type GitLabProvider struct { | ||
BaseProvider | ||
project string // URL-encoded project path with namespace (e.g., "group/project") | ||
} | ||
|
||
// NewGitLabProvider creates a new GitLab provider with the given configuration | ||
func NewGitLabProvider(config Config) *GitLabProvider { | ||
project := parseGitLabURL(config.URL) | ||
return &GitLabProvider{ | ||
BaseProvider: NewBaseProvider(config), | ||
project: project, | ||
} | ||
} | ||
|
||
// createRequest creates a GitLab API request with appropriate headers | ||
func (g *GitLabProvider) createRequest(issueNumber string) (*http.Request, error) { | ||
url := fmt.Sprintf("https://gitlab.com/api/v4/projects/%s/issues/%s", | ||
url.PathEscape(g.project), issueNumber) | ||
|
||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create request: %w", err) | ||
} | ||
|
||
if g.config.Token != "" { | ||
req.Header.Set("PRIVATE-TOKEN", g.config.Token) | ||
} | ||
|
||
return req, nil | ||
} | ||
|
||
// GetIssue fetches issue details from GitLab | ||
func (g *GitLabProvider) GetIssue(issueNumber string) (*Issue, error) { | ||
type GitLabIssue struct { | ||
IID int `json:"iid"` | ||
Title string `json:"title"` | ||
Description string `json:"description"` | ||
} | ||
req, err := g.createRequest(issueNumber) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := g.doRequest(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if resp == nil { | ||
return nil, nil | ||
} | ||
defer resp.Body.Close() | ||
|
||
var gitlabIssue GitLabIssue | ||
if err := json.NewDecoder(resp.Body).Decode(&gitlabIssue); err != nil { | ||
return nil, fmt.Errorf("failed to decode response: %w", err) | ||
} | ||
|
||
return &Issue{ | ||
Number: gitlabIssue.IID, | ||
Title: gitlabIssue.Title, | ||
Body: gitlabIssue.Description, | ||
}, nil | ||
} | ||
|
||
// parseGitLabURL extracts project path from a GitLab URL | ||
func parseGitLabURL(url string) string { | ||
url = strings.TrimSuffix(strings.TrimSuffix(url, "/"), ".git") | ||
parts := strings.Split(url, "/") | ||
for i, part := range parts { | ||
if part == "gitlab.com" && i+1 < len(parts) { | ||
return strings.Join(parts[i+1:], "/") | ||
} | ||
} | ||
return "" | ||
} |
Oops, something went wrong.