Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions github/repos_contents.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import (
"strings"
)

// ErrContentsDirectory indicates that the contents are not available for a directory.
var ErrContentsDirectory = errors.New("contents not available for directory")

// ErrContentsSubmodule indicates that the contents are not available for a submodule.
var ErrContentsSubmodule = errors.New("contents not available for submodule")

// ErrContentsNoDownloadURL indicates that the contents download URL is empty, which may occur when file size > 100 MB.
var ErrContentsNoDownloadURL = errors.New("contents download url is empty")

// RepositoryContent represents a file or directory in a github repository.
type RepositoryContent struct {
Type *string `json:"type,omitempty"`
Expand Down Expand Up @@ -130,6 +139,10 @@ func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string,
// returned error is nil. Callers should check the returned Response status
// code to verify the content is from a successful response.
//
// DownloadContents returns [ErrContentsDirectory] if the path references a
// directory, [ErrContentsSubmodule] if the path references a submodule, and
// [ErrContentsNoDownloadURL] if the file's download URL is empty.
//
// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-repository-content
//
//meta:operation GET /repos/{owner}/{repo}/contents/{path}
Expand All @@ -147,6 +160,11 @@ func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo,
// returned error is nil. Callers should check the returned Response status
// code to verify the content is from a successful response.
//
// DownloadContentsWithMeta returns [ErrContentsDirectory] if the path
// references a directory, [ErrContentsSubmodule] if the path references a
// submodule, and [ErrContentsNoDownloadURL] if the file's download URL is
// empty.
//
// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-repository-content
//
//meta:operation GET /repos/{owner}/{repo}/contents/{path}
Expand All @@ -157,7 +175,11 @@ func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owne
}

if fileContent == nil {
return nil, nil, resp, errors.New("no file content found")
return nil, nil, resp, ErrContentsDirectory
}

if fileContent.GetType() == "submodule" {
return nil, fileContent, resp, ErrContentsSubmodule
}

content, err := fileContent.GetContent()
Expand All @@ -167,7 +189,7 @@ func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owne

downloadURL := fileContent.GetDownloadURL()
if downloadURL == "" {
return nil, fileContent, resp, errors.New("download url is empty")
return nil, fileContent, resp, ErrContentsNoDownloadURL
}

dlReq, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil)
Expand Down
164 changes: 122 additions & 42 deletions github/repos_contents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func TestRepositoriesService_DownloadContents_SuccessByDownload(t *testing.T) {
})
}

func TestRepositoriesService_DownloadContents_FailedResponse(t *testing.T) {
func TestRepositoriesService_DownloadContents_FailedDownloadResponse(t *testing.T) {
t.Parallel()
client, mux, serverURL := setup(t)

Expand Down Expand Up @@ -269,26 +269,50 @@ func TestRepositoriesService_DownloadContents_FailedResponse(t *testing.T) {
}
}

func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
func TestRepositoriesService_DownloadContents_NotFound(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{
w.WriteHeader(http.StatusNotFound)
})

ctx := t.Context()
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
if err == nil {
t.Error("Repositories.DownloadContents did not return expected error")
}

if reader != nil {
t.Error("Repositories.DownloadContents did not return expected reader")
}

if resp == nil || resp.Response.StatusCode != http.StatusNotFound {
t.Error("Repositories.DownloadContents did not return expected response")
}
}

func TestRepositoriesService_DownloadContents_NotFile(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{
"type": "file",
"name": "f",
"content": ""
}`)
}]`)
})

ctx := t.Context()
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
if err == nil {
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d", nil)
if err == nil || !errors.Is(err, ErrContentsDirectory) {
t.Error("Repositories.DownloadContents did not return expected error")
}

if resp == nil {
if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContents did not return expected response")
}

Expand All @@ -297,22 +321,28 @@ func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
}
}

func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
func TestRepositoriesService_DownloadContents_Submodule(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, `{
"type": "submodule",
"name": "f",
"content": "",
"download_url": "",
"submodule_git_url": "http://example.com/submodule.git"
}`)
})

ctx := t.Context()
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
if err == nil {
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d", nil)
if err == nil || !errors.Is(err, ErrContentsSubmodule) {
t.Error("Repositories.DownloadContents did not return expected error")
}

if resp == nil {
if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContents did not return expected response")
}

Expand All @@ -321,26 +351,26 @@ func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
}
}

func TestRepositoriesService_DownloadContents_NotFile(t *testing.T) {
func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{
fmt.Fprint(w, `{
"type": "file",
"name": "f",
"content": ""
}]`)
}`)
})

ctx := t.Context()
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d", nil)
if err == nil {
reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
if err == nil || !errors.Is(err, ErrContentsNoDownloadURL) {
t.Error("Repositories.DownloadContents did not return expected error")
}

if resp == nil {
if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContents did not return expected response")
}

Expand Down Expand Up @@ -456,7 +486,7 @@ func TestRepositoriesService_DownloadContentsWithMeta_SuccessByDownload(t *testi
}
}

func TestRepositoriesService_DownloadContentsWithMeta_FailedResponse(t *testing.T) {
func TestRepositoriesService_DownloadContentsWithMeta_FailedDownloadResponse(t *testing.T) {
t.Parallel()
client, mux, serverURL := setup(t)

Expand Down Expand Up @@ -503,17 +533,13 @@ func TestRepositoriesService_DownloadContentsWithMeta_FailedResponse(t *testing.
}
}

func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) {
func TestRepositoriesService_DownloadContentsWithMeta_NotFound(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `{
"type": "file",
"name": "f",
"content": ""
}`)
w.WriteHeader(http.StatusNotFound)
})

ctx := t.Context()
Expand All @@ -526,55 +552,109 @@ func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T
t.Error("Repositories.DownloadContentsWithMeta did not return expected reader")
}

if resp == nil {
if contents != nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected content")
}

if resp == nil || resp.Response.StatusCode != http.StatusNotFound {
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
}
}

if contents == nil {
func TestRepositoriesService_DownloadContentsWithMeta_NotFile(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{
"type": "file",
"name": "f",
"content": ""
}]`)
})

ctx := t.Context()
reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil)
if err == nil || !errors.Is(err, ErrContentsDirectory) {
t.Error("Repositories.DownloadContentsWithMeta did not return expected error")
}

if reader != nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected reader")
}

if contents != nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected content")
}

if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
}
}

func TestRepositoriesService_DownloadContentsWithMeta_NoFile(t *testing.T) {
func TestRepositoriesService_DownloadContentsWithMeta_Submodule(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, `{
"type": "submodule",
"name": "f",
"content": "",
"download_url": "",
"submodule_git_url": "http://example.com/submodule.git"
}`)
})

ctx := t.Context()
_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
if err == nil {
reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil)
if err == nil || !errors.Is(err, ErrContentsSubmodule) {
t.Error("Repositories.DownloadContentsWithMeta did not return expected error")
}

if resp == nil {
if reader != nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected reader")
}

if contents == nil || contents.GetType() != "submodule" {
t.Error("Repositories.DownloadContentsWithMeta did not return expected content")
}

if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
}
}

func TestRepositoriesService_DownloadContentsWithMeta_NotFile(t *testing.T) {
func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) {
t.Parallel()
client, mux, _ := setup(t)

mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
fmt.Fprint(w, `[{
fmt.Fprint(w, `{
"type": "file",
"name": "f",
"content": ""
}]`)
}`)
})

ctx := t.Context()
_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil)
if err == nil {
reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
if err == nil || !errors.Is(err, ErrContentsNoDownloadURL) {
t.Error("Repositories.DownloadContentsWithMeta did not return expected error")
}

if resp == nil {
if reader != nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected reader")
}

if contents == nil {
t.Error("Repositories.DownloadContentsWithMeta did not return expected content")
}

if resp == nil || resp.Response.StatusCode != http.StatusOK {
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
}
}
Expand Down
Loading