chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
// Package raw provides a client for interacting with the GitHub raw file API
|
||||
package raw
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
gogithub "github.com/google/go-github/v89/github"
|
||||
)
|
||||
|
||||
// GetRawClientFn is a function type that returns a RawClient instance.
|
||||
type GetRawClientFn func(context.Context) (*Client, error)
|
||||
|
||||
// Client is a client for interacting with the GitHub raw content API.
|
||||
type Client struct {
|
||||
url *url.URL
|
||||
client *gogithub.Client
|
||||
}
|
||||
|
||||
// NewClient creates a new instance of the raw API Client with the provided GitHub client and provided URL.
|
||||
func NewClient(client *gogithub.Client, rawURL *url.URL) (*Client, error) {
|
||||
newClient, err := gogithub.NewClient(
|
||||
gogithub.WithHTTPClient(client.Client()),
|
||||
gogithub.WithEnterpriseURLs(rawURL.String(), rawURL.String()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{client: newClient, url: rawURL}, nil
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(ctx context.Context, method string, urlStr string, body any, opts ...gogithub.RequestOption) (*http.Request, error) {
|
||||
return c.client.NewRequest(ctx, method, urlStr, body, opts...)
|
||||
}
|
||||
|
||||
func (c *Client) refURL(owner, repo, ref, path string) string {
|
||||
if ref == "" {
|
||||
return c.url.JoinPath(owner, repo, "HEAD", path).String()
|
||||
}
|
||||
return c.url.JoinPath(owner, repo, ref, path).String()
|
||||
}
|
||||
|
||||
func (c *Client) URLFromOpts(opts *ContentOpts, owner, repo, path string) string {
|
||||
if opts == nil {
|
||||
opts = &ContentOpts{}
|
||||
}
|
||||
if opts.SHA != "" {
|
||||
return c.commitURL(owner, repo, opts.SHA, path)
|
||||
}
|
||||
return c.refURL(owner, repo, opts.Ref, path)
|
||||
}
|
||||
|
||||
// BlobURL returns the URL for a blob in the raw content API.
|
||||
func (c *Client) commitURL(owner, repo, sha, path string) string {
|
||||
return c.url.JoinPath(owner, repo, sha, path).String()
|
||||
}
|
||||
|
||||
type ContentOpts struct {
|
||||
Ref string
|
||||
SHA string
|
||||
}
|
||||
|
||||
// GetRawContent fetches the raw content of a file from a GitHub repository.
|
||||
func (c *Client) GetRawContent(ctx context.Context, owner, repo, path string, opts *ContentOpts) (*http.Response, error) {
|
||||
url := c.URLFromOpts(opts, owner, repo, path)
|
||||
req, err := c.newRequest(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.client.Client().Do(req)
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v89/github"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// mockRawTransport is a custom HTTP transport for testing raw content API
|
||||
type mockRawTransport struct {
|
||||
statusCode int
|
||||
contentType string
|
||||
body string
|
||||
}
|
||||
|
||||
func (m *mockRawTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// Create a response with the configured status and body
|
||||
resp := &http.Response{
|
||||
StatusCode: m.statusCode,
|
||||
Header: make(http.Header),
|
||||
Body: io.NopCloser(bytes.NewBufferString(m.body)),
|
||||
Request: req,
|
||||
}
|
||||
if m.contentType != "" {
|
||||
resp.Header.Set("Content-Type", m.contentType)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func TestGetRawContent(t *testing.T) {
|
||||
base, _ := url.Parse("https://raw.example.com/")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *ContentOpts
|
||||
owner, repo, path string
|
||||
statusCode int
|
||||
contentType string
|
||||
body string
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
name: "HEAD fetch success",
|
||||
opts: nil,
|
||||
owner: "octocat",
|
||||
repo: "hello",
|
||||
path: "README.md",
|
||||
statusCode: 200,
|
||||
contentType: "text/plain",
|
||||
body: "# Test file",
|
||||
},
|
||||
{
|
||||
name: "branch fetch success",
|
||||
opts: &ContentOpts{Ref: "refs/heads/main"},
|
||||
owner: "octocat",
|
||||
repo: "hello",
|
||||
path: "README.md",
|
||||
statusCode: 200,
|
||||
contentType: "text/plain",
|
||||
body: "# Test file",
|
||||
},
|
||||
{
|
||||
name: "tag fetch success",
|
||||
opts: &ContentOpts{Ref: "refs/tags/v1.0.0"},
|
||||
owner: "octocat",
|
||||
repo: "hello",
|
||||
path: "README.md",
|
||||
statusCode: 200,
|
||||
contentType: "text/plain",
|
||||
body: "# Test file",
|
||||
},
|
||||
{
|
||||
name: "sha fetch success",
|
||||
opts: &ContentOpts{SHA: "abc123"},
|
||||
owner: "octocat",
|
||||
repo: "hello",
|
||||
path: "README.md",
|
||||
statusCode: 200,
|
||||
contentType: "text/plain",
|
||||
body: "# Test file",
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
opts: nil,
|
||||
owner: "octocat",
|
||||
repo: "hello",
|
||||
path: "notfound.txt",
|
||||
statusCode: 404,
|
||||
contentType: "application/json",
|
||||
body: `{"message": "Not Found"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create mock HTTP client with custom transport
|
||||
mockedClient := &http.Client{
|
||||
Transport: &mockRawTransport{
|
||||
statusCode: tc.statusCode,
|
||||
contentType: tc.contentType,
|
||||
body: tc.body,
|
||||
},
|
||||
}
|
||||
ghClient, err := github.NewClient(github.WithHTTPClient(mockedClient))
|
||||
require.NoError(t, err)
|
||||
client, err := NewClient(ghClient, base)
|
||||
require.NoError(t, err)
|
||||
resp, err := client.GetRawContent(context.Background(), tc.owner, tc.repo, tc.path, tc.opts)
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if tc.expectError != "" {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.statusCode, resp.StatusCode)
|
||||
|
||||
// Verify the URL was constructed correctly
|
||||
actualURL := client.URLFromOpts(tc.opts, tc.owner, tc.repo, tc.path)
|
||||
require.True(t, strings.Contains(actualURL, tc.owner))
|
||||
require.True(t, strings.Contains(actualURL, tc.repo))
|
||||
require.True(t, strings.Contains(actualURL, tc.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUrlFromOpts(t *testing.T) {
|
||||
base, _ := url.Parse("https://raw.example.com/")
|
||||
ghClient, err := github.NewClient(github.WithHTTPClient(&http.Client{}))
|
||||
require.NoError(t, err)
|
||||
client, err := NewClient(ghClient, base)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *ContentOpts
|
||||
owner string
|
||||
repo string
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "no opts (HEAD)",
|
||||
opts: nil,
|
||||
owner: "octocat", repo: "hello", path: "README.md",
|
||||
want: "https://raw.example.com/octocat/hello/HEAD/README.md",
|
||||
},
|
||||
{
|
||||
name: "ref branch",
|
||||
opts: &ContentOpts{Ref: "refs/heads/main"},
|
||||
owner: "octocat", repo: "hello", path: "README.md",
|
||||
want: "https://raw.example.com/octocat/hello/refs/heads/main/README.md",
|
||||
},
|
||||
{
|
||||
name: "ref tag",
|
||||
opts: &ContentOpts{Ref: "refs/tags/v1.0.0"},
|
||||
owner: "octocat", repo: "hello", path: "README.md",
|
||||
want: "https://raw.example.com/octocat/hello/refs/tags/v1.0.0/README.md",
|
||||
},
|
||||
{
|
||||
name: "sha",
|
||||
opts: &ContentOpts{SHA: "abc123"},
|
||||
owner: "octocat", repo: "hello", path: "README.md",
|
||||
want: "https://raw.example.com/octocat/hello/abc123/README.md",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := client.URLFromOpts(tt.opts, tt.owner, tt.repo, tt.path)
|
||||
if got != tt.want {
|
||||
t.Errorf("UrlFromOpts() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user