diff --git a/github/data_source_github_repository_file.go b/github/data_source_github_repository_file.go index 52608c1e32..a895444101 100644 --- a/github/data_source_github_repository_file.go +++ b/github/data_source_github_repository_file.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "strings" "github.com/google/go-github/v49/github" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -64,12 +65,23 @@ func dataSourceGithubRepositoryFile() *schema.Resource { } func dataSourceGithubRepositoryFileRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*Owner).v3client - owner := meta.(*Owner).name ctx := context.WithValue(context.Background(), ctxId, d.Id()) + owner := meta.(*Owner).name repo := d.Get("repository").(string) + + // checking if repo has a slash in it, which means that full_name was passed + // split and replace owner and repo + parts := strings.Split(repo, "/") + if len(parts) == 2 { + log.Printf("[DEBUG] repo has a slash, extracting owner from: %s", repo) + owner = parts[0] + repo = parts[1] + + log.Printf("[DEBUG] owner: %s repo:%s", owner, repo) + } + file := d.Get("file").(string) branch := d.Get("branch").(string) if err := checkRepositoryBranchExists(client, owner, repo, branch); err != nil { diff --git a/github/data_source_github_repository_file_test.go b/github/data_source_github_repository_file_test.go index ac1fe4a9bc..602b8d07ea 100644 --- a/github/data_source_github_repository_file_test.go +++ b/github/data_source_github_repository_file_test.go @@ -1,19 +1,25 @@ package github import ( + "encoding/base64" + "encoding/json" "fmt" + "net/http" + "net/url" "testing" + "github.com/google/go-github/v49/github" + "github.com/stretchr/testify/assert" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) func TestAccGithubRepositoryFileDataSource(t *testing.T) { - randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) t.Run("read files", func(t *testing.T) { - config := fmt.Sprintf(` resource "github_repository" "test" { @@ -85,6 +91,191 @@ func TestAccGithubRepositoryFileDataSource(t *testing.T) { t.Run("with an organization account", func(t *testing.T) { testCase(t, organization) }) + }) +} + +func TestDataSourceGithubRepositoryFileRead(t *testing.T) { + // helper function to simplify marshalling. + marshal := func(t *testing.T, msg interface{}) string { + data, err := json.MarshalIndent(msg, "", " ") + if err != nil { + t.Fatalf("cant encode to json: %v", err) + } + + return string(data) + } + + sha := "some-test-sha" + committerName := "some-test-user" + committerEmail := "some-test-user@github.com" + commitMessage := "test commit message" + + enc := "base64" + fileContent := "here-goes-content-of-our-glorious-config.json" + b64FileContent := base64.StdEncoding.EncodeToString([]byte(fileContent)) + + fileName := "test-file.json" + branch := "main" + + // setting up some org/owner info + owner := "test-owner" + org := "test-org" + repo := "test-repo" + + // preparing mashalled objects + branchRespBody := marshal(t, &github.Branch{Name: &branch}) + repoContentRespBody := marshal(t, &github.RepositoryContent{ + Encoding: &enc, + Content: &b64FileContent, + SHA: &sha, + }) + repoCommitRespBody := marshal(t, &github.RepositoryCommit{ + SHA: &sha, + Committer: &github.User{ + Name: &committerName, + Email: &committerEmail, + }, + Commit: &github.Commit{ + Message: &commitMessage, + }, + }) + + t.Run("extracting org and repo if full_name is passed", func(t *testing.T) { + // test setup + repositoryFullName := fmt.Sprintf("%s/%s", org, repo) + expectedID := fmt.Sprintf("%s/%s", repo, fileName) + expectedRepo := "test-repo" + + ts := githubApiMock([]*mockResponse{ + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/branches/%s", org, repo, branch), + ResponseBody: branchRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", org, repo, fileName, branch), + ResponseBody: repoContentRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/commits/%s", org, repo, sha), + ResponseBody: repoCommitRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/commits", org, repo), + ResponseBody: repoCommitRespBody, + StatusCode: http.StatusOK, + }, + }) + defer ts.Close() + + httpCl := http.DefaultClient + httpCl.Transport = http.DefaultTransport + + client := github.NewClient(httpCl) + u, _ := url.Parse(ts.URL + "/") + client.BaseURL = u + + meta := &Owner{ + name: owner, + v3client: client, + } + + testSchema := map[string]*schema.Schema{ + "repository": {Type: schema.TypeString}, + "file": {Type: schema.TypeString}, + "branch": {Type: schema.TypeString}, + "commit_sha": {Type: schema.TypeString}, + "content": {Type: schema.TypeString}, + "id": {Type: schema.TypeString}, + } + + schema := schema.TestResourceDataRaw(t, testSchema, map[string]interface{}{ + "repository": repositoryFullName, + "file": fileName, + "branch": branch, + "commit_sha": sha, + "content": "", + "id": "", + }) + + // actual call + err := dataSourceGithubRepositoryFileRead(schema, meta) + + // assertions + assert.Nil(t, err) + assert.Equal(t, expectedRepo, schema.Get("repository")) + assert.Equal(t, fileContent, schema.Get("content")) + assert.Equal(t, expectedID, schema.Get("id")) + }) + t.Run("using user as owner if just name is passed", func(t *testing.T) { + // test setup + repositoryFullName := repo + expectedID := fmt.Sprintf("%s/%s", repo, fileName) + expectedRepo := "test-repo" + + ts := githubApiMock([]*mockResponse{ + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/branches/%s", owner, repo, branch), + ResponseBody: branchRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, fileName, branch), + ResponseBody: repoContentRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/commits/%s", owner, repo, sha), + ResponseBody: repoCommitRespBody, + StatusCode: http.StatusOK, + }, + { + ExpectedUri: fmt.Sprintf("/repos/%s/%s/commits", owner, repo), + ResponseBody: repoCommitRespBody, + StatusCode: http.StatusOK, + }, + }) + defer ts.Close() + + httpCl := http.DefaultClient + httpCl.Transport = http.DefaultTransport + + client := github.NewClient(httpCl) + u, _ := url.Parse(ts.URL + "/") + client.BaseURL = u + + meta := &Owner{ + name: owner, + v3client: client, + } + + testSchema := map[string]*schema.Schema{ + "repository": {Type: schema.TypeString}, + "file": {Type: schema.TypeString}, + "branch": {Type: schema.TypeString}, + "commit_sha": {Type: schema.TypeString}, + "content": {Type: schema.TypeString}, + "id": {Type: schema.TypeString}, + } + + schema := schema.TestResourceDataRaw(t, testSchema, map[string]interface{}{ + "repository": repositoryFullName, + "file": fileName, + "branch": branch, + "commit_sha": sha, + "content": "", + "id": "", + }) + + // actual call + err := dataSourceGithubRepositoryFileRead(schema, meta) + // assertions + assert.Nil(t, err) + assert.Equal(t, expectedRepo, schema.Get("repository")) + assert.Equal(t, fileContent, schema.Get("content")) + assert.Equal(t, expectedID, schema.Get("id")) }) } diff --git a/website/docs/d/repository_file.html.markdown b/website/docs/d/repository_file.html.markdown index 069c11dcb4..d7d9ae4b07 100644 --- a/website/docs/d/repository_file.html.markdown +++ b/website/docs/d/repository_file.html.markdown @@ -27,12 +27,12 @@ data "github_repository_file" "foo" { The following arguments are supported: -* `repository` - (Required) The repository to create the file in. +* `repository` - (Required) The repository to read the file from. If an unqualified repo name (without an owner) is passed, the owner will be inferred from the owner of the token used to execute the plan. If a name of the type "owner/repo" (with a slash in the middle) is passed, the owner will be as specified and not the owner of the token. * `file` - (Required) The path of the file to manage. -* `branch` - (Optional) Git branch (defaults to `main`). - The branch must already exist, it will not be created if it does not already exist. +* `branch` - (Optional) Git branch (if omitted, the default repository branch is used, which is usually `main`) + The branch must already exist; it will not be created if it does not already exist. ## Attributes Reference