Skip to content

Commit

Permalink
If applied, this commit will add tree client to github gitprovider
Browse files Browse the repository at this point in the history
Add create,get, and list functionalities to tree client of github

not implemented in gitlab and stash clients

Signed-off-by: Rana Tarek Hassan <[email protected]>
  • Loading branch information
ranatrk committed Jul 17, 2022
1 parent b5aa6a0 commit 5ae2be2
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 1 deletion.
150 changes: 150 additions & 0 deletions github/client_repository_tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package github

import (
"context"

"github.com/fluxcd/go-git-providers/gitprovider"
"github.com/google/go-github/v42/github"
)

// TreeClient implements the gitprovider.TreeClient interface.
var _ gitprovider.TreeClient = &TreeClient{}

// TreeClient operates on the trees in a specific repository.
type TreeClient struct {
*clientContext
ref gitprovider.RepositoryRef
}

// Create creates,updates,deletes a tree
func (c *TreeClient) Create(ctx context.Context, tree *gitprovider.TreeInfo) (*gitprovider.TreeInfo, error) {
repoName := c.ref.GetRepository()
repoOwner := c.ref.GetIdentity()

treeEntries := make([]*github.TreeEntry, 0)
for _, treeEntry := range tree.Tree {
treeEntries = append(treeEntries, &github.TreeEntry{
Path: &treeEntry.Path,
Mode: &treeEntry.Mode,
Type: &treeEntry.Type,
Size: &treeEntry.Size,
SHA: &treeEntry.SHA,
URL: &treeEntry.URL,
})
}
githubTree, _, err := c.c.Client().Git.CreateTree(ctx, repoOwner, repoName, tree.SHA, treeEntries)
if err != nil {
return nil, err
}

responseTreeEntries := make([]*gitprovider.TreeEntry, 0)
for _, responseTreeEntry := range githubTree.Entries {
size := 0
if *responseTreeEntry.Type != "tree" {
size = *responseTreeEntry.Size
}
responseTreeEntries = append(responseTreeEntries, &gitprovider.TreeEntry{
Path: *responseTreeEntry.Path,
Mode: *responseTreeEntry.Mode,
Type: *responseTreeEntry.Type,
Size: size,
SHA: *responseTreeEntry.SHA,
URL: *responseTreeEntry.URL,
})
}

responseTreeInfo := gitprovider.TreeInfo{
SHA: *githubTree.SHA,
Tree: responseTreeEntries,
Truncated: *githubTree.Truncated,
}

return &responseTreeInfo, nil
}

// Get returns a tree
func (c *TreeClient) Get(ctx context.Context, sha string, recursive bool) (*gitprovider.TreeInfo, error) {
// GET /repos/{owner}/{repo}/git/trees
repoName := c.ref.GetRepository()
repoOwner := c.ref.GetIdentity()
githubTree, _, err := c.c.Client().Git.GetTree(ctx, repoOwner, repoName, sha, true)
if err != nil {
return nil, err
}

treeEntries := make([]*gitprovider.TreeEntry, 0)
for _, treeEntry := range githubTree.Entries {
size := 0
if *treeEntry.Type != "tree" {
size = *treeEntry.Size
}
treeEntries = append(treeEntries, &gitprovider.TreeEntry{
Path: *treeEntry.Path,
Mode: *treeEntry.Mode,
Type: *treeEntry.Type,
Size: size,
SHA: *treeEntry.SHA,
URL: *treeEntry.URL,
})
}

treeInfo := gitprovider.TreeInfo{
SHA: *githubTree.SHA,
Tree: treeEntries,
Truncated: *githubTree.Truncated,
}

return &treeInfo, nil

}

// List files (blob) in a tree
func (c *TreeClient) List(ctx context.Context, sha string, recursive bool) ([]*gitprovider.TreeEntry, error) {
treeInfo, err := c.Get(ctx, sha, recursive)
if err != nil {
return nil, err
}
treeEntries := make([]*gitprovider.TreeEntry, 0)
for _, treeEntry := range treeInfo.Tree {
if treeEntry.Type == "blob" {
treeEntries = append(treeEntries, &gitprovider.TreeEntry{
Path: treeEntry.Path,
Mode: treeEntry.Mode,
Type: treeEntry.Type,
Size: treeEntry.Size,
SHA: treeEntry.SHA,
URL: treeEntry.URL,
})
}
}

return treeEntries, nil
}

func createTreeEntry(githubTreeEntry github.TreeEntry) *gitprovider.TreeEntry {
newTreeEntry := gitprovider.TreeEntry{
Path: *githubTreeEntry.Path,
Mode: *githubTreeEntry.Mode,
Type: *githubTreeEntry.Type,
Size: *githubTreeEntry.Size,
SHA: *githubTreeEntry.SHA,
URL: *githubTreeEntry.URL,
}
return &newTreeEntry
}
81 changes: 81 additions & 0 deletions github/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,87 @@ var _ = Describe("GitHub Provider", func() {

})

It("should be possible to get and list repo tree", func() {

userRepoRef := newUserRepoRef(testUser, testUserRepoName)

userRepo, err := c.UserRepositories().Get(ctx, userRepoRef)
Expect(err).ToNot(HaveOccurred())

defaultBranch := userRepo.Get().DefaultBranch

path0 := "clustersDir/cluster/machine.yaml"
content0 := "machine yaml content"
path1 := "clustersDir/cluster/machine1.yaml"
content1 := "machine1 yaml content"
path2 := "clustersDir/cluster2/clusterSubDir/machine2.yaml"
content2 := "machine2 yaml content"

files := []gitprovider.CommitFile{
{
Path: &path0,
Content: &content0,
},
{
Path: &path1,
Content: &content1,
},
{
Path: &path2,
Content: &content2,
},
}

commitFiles := make([]gitprovider.CommitFile, 0)
for _, file := range files {
path := file.Path
content := file.Content
commitFiles = append(commitFiles, gitprovider.CommitFile{
Path: path,
Content: content,
})
}

commit, err := userRepo.Commits().Create(ctx, *defaultBranch, "added files", commitFiles)
Expect(err).ToNot(HaveOccurred())
commitSha := commit.Get().Sha

// get tree
tree, err := userRepo.Trees().Get(ctx, commitSha, true)
Expect(err).ToNot(HaveOccurred())

// Tree should have length 9 for : LISENCE, README.md, 3 blob (files), 4 tree (directories)
Expect(tree.Tree).To(HaveLen(9))

// itemsToBeIgnored initially with 2 for LICENSE and README.md, and will also include tree types
itemsToBeIgnored := 2
for ind, treeEntry := range tree.Tree {
if treeEntry.Type == "blob" {
if treeEntry.Path == "LICENSE" || treeEntry.Path == "README.md" {
continue
}
Expect(*&treeEntry.Path).To(Equal(*files[ind-itemsToBeIgnored].Path))
continue

}
itemsToBeIgnored += 1
}

// List tree items
treeEntries, err := userRepo.Trees().List(ctx, commitSha, true)
Expect(err).ToNot(HaveOccurred())

// Tree Entries should have length 5 for : LISENCE, README.md, 3 blob (files)
Expect(treeEntries).To(HaveLen(5))
for ind, treeEntry := range treeEntries {
if treeEntry.Path == "LICENSE" || treeEntry.Path == "README.md" {
continue
}
Expect(*&treeEntry.Path).To(Equal(*files[ind-2].Path))
}

})

AfterSuite(func() {
if os.Getenv("SKIP_CLEANUP") == "1" {
return
Expand Down
9 changes: 9 additions & 0 deletions github/resource_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func newUserRepository(ctx *clientContext, apiObj *github.Repository, ref gitpro
clientContext: ctx,
ref: ref,
},
trees: &TreeClient{
clientContext: ctx,
ref: ref,
},
}
}

Expand All @@ -68,6 +72,7 @@ type userRepository struct {
branches *BranchClient
pullRequests *PullRequestClient
files *FileClient
trees *TreeClient
}

func (r *userRepository) Get() gitprovider.RepositoryInfo {
Expand Down Expand Up @@ -110,6 +115,10 @@ func (r *userRepository) Files() gitprovider.FileClient {
return r.files
}

func (r *userRepository) Trees() gitprovider.TreeClient {
return r.trees
}

// Update will apply the desired state in this object to the server.
// Only set fields will be respected (i.e. PATCH behaviour).
// In order to apply changes to this object, use the .Set({Resource}Info) error
Expand Down
50 changes: 50 additions & 0 deletions gitlab/client_repository_tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2020 The Flux CD contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gitlab

import (
"context"
"fmt"

"github.com/fluxcd/go-git-providers/gitprovider"
)

// TreeClient implements the gitprovider.TreeClient interface.
var _ gitprovider.TreeClient = &TreeClient{}

// TreeClient operates on the trees in a specific repository.
type TreeClient struct {
*clientContext
ref gitprovider.RepositoryRef
}

// Create creates,updates,deletes a tree
func (c *TreeClient) Create(ctx context.Context, tree *gitprovider.TreeInfo) (*gitprovider.TreeInfo, error) {
return nil, fmt.Errorf("error creaing tree %s. not implemented in gitlab yet", tree.SHA)

}

// Get returns a tree
func (c *TreeClient) Get(ctx context.Context, sha string, recursive bool) (*gitprovider.TreeInfo, error) {
return nil, fmt.Errorf("error getting tree %s. not implemented in gitlab yet", sha)

}

// List files (blob) in a tree
func (c *TreeClient) List(ctx context.Context, sha string, recursive bool) ([]*gitprovider.TreeEntry, error) {
return nil, fmt.Errorf("error listing tree items %s. not implemented in gitlab yet", sha)
}
9 changes: 9 additions & 0 deletions gitlab/resource_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func newUserProject(ctx *clientContext, apiObj *gogitlab.Project, ref gitprovide
clientContext: ctx,
ref: ref,
},
trees: &TreeClient{
clientContext: ctx,
ref: ref,
},
}
}

Expand All @@ -67,6 +71,7 @@ type userProject struct {
branches *BranchClient
pullRequests *PullRequestClient
files *FileClient
trees *TreeClient
}

func (p *userProject) Get() gitprovider.RepositoryInfo {
Expand Down Expand Up @@ -109,6 +114,10 @@ func (p *userProject) Files() gitprovider.FileClient {
return p.files
}

func (p *userProject) Trees() gitprovider.TreeClient {
return p.trees
}

// The internal API object will be overridden with the received server data.
func (p *userProject) Update(ctx context.Context) error {
// PATCH /repos/{owner}/{repo}
Expand Down
9 changes: 9 additions & 0 deletions gitprovider/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,12 @@ type FileClient interface {
// GetFiles fetch files content from specific path and branch
Get(ctx context.Context, path, branch string, optFns ...FilesGetOption) ([]*CommitFile, error)
}

type TreeClient interface {
// Create allows for creating or editing tree
Create(ctx context.Context, tree *TreeInfo) (*TreeInfo, error)
// Get retrieves tree information and items
Get(ctx context.Context, sha string, recursive bool) (*TreeInfo, error)
// List retrieves list of tree files (files/blob)
List(ctx context.Context, sha string, recursive bool) ([]*TreeEntry, error)
}
16 changes: 15 additions & 1 deletion gitprovider/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@ type UserRepository interface {
// PullRequests gives access to this specific repository pull requests
PullRequests() PullRequestClient

// Files gives access to this specific repository pull requests
// Files gives access to this specific repository files
Files() FileClient

// Trees gives access to this specific repository trees
Trees() TreeClient
}

// OrgRepository describes a repository owned by an organization.
Expand Down Expand Up @@ -156,3 +159,14 @@ type PullRequest interface {
// Get returns high-level information about this pull request.
Get() PullRequestInfo
}

type Tree interface {
// Object implements the Object interface,
// allowing access to the underlying object returned from the API.
Object

// Get returns high-level information about this tree.
Create() TreeInfo
Get() TreeInfo
List() TreeEntry
}
Loading

0 comments on commit 5ae2be2

Please sign in to comment.