From 7ceebcb915c9adb4ba35b7f132f6e4b5c169bacd Mon Sep 17 00:00:00 2001 From: Noah Lee Date: Mon, 27 May 2024 10:27:06 +0900 Subject: [PATCH 1/4] feat: Add GetDefaultBranch method to BranchSCM interface --- internal/interactor/interface.go | 1 + internal/pkg/github/repos.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/internal/interactor/interface.go b/internal/interactor/interface.go index 4b187f12..31fd4f50 100644 --- a/internal/interactor/interface.go +++ b/internal/interactor/interface.go @@ -61,6 +61,7 @@ type ( BranchSCM interface { ListBranches(ctx context.Context, u *ent.User, r *ent.Repo, opt *ListOptions) ([]*extent.Branch, error) GetBranch(ctx context.Context, u *ent.User, r *ent.Repo, branch string) (*extent.Branch, error) + GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) } TagSCM interface { diff --git a/internal/pkg/github/repos.go b/internal/pkg/github/repos.go index 4467177b..94e3c31a 100644 --- a/internal/pkg/github/repos.go +++ b/internal/pkg/github/repos.go @@ -157,6 +157,26 @@ func (g *Github) GetBranch(ctx context.Context, u *ent.User, r *ent.Repo, branch return mapGithubBranchToBranch(b), nil } +func (g *Github) GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) { + rr, res, err := g.Client(ctx, u.Token).Repositories.Get(ctx, r.Namespace, r.Name) + if res.StatusCode == http.StatusNotFound { + return nil, e.NewErrorWithMessage(e.ErrorCodeEntityNotFound, "The default branch is not found.", err) + } else if err != nil { + return nil, e.NewError(e.ErrorCodeInternalError, err) + } + + b, res, err := g.Client(ctx, u.Token). + Repositories. + GetBranch(ctx, r.Namespace, r.Name, *rr.DefaultBranch, false) + if res.StatusCode == http.StatusNotFound { + return nil, e.NewErrorWithMessage(e.ErrorCodeEntityNotFound, "The default branch is not found.", err) + } else if err != nil { + return nil, e.NewError(e.ErrorCodeInternalError, err) + } + + return mapGithubBranchToBranch(b), nil +} + // ListTags list up tags as ordered by commit date. // Github GraphQL explore - https://docs.github.com/en/graphql/overview/explorer func (g *Github) ListTags(ctx context.Context, u *ent.User, r *ent.Repo, opt *i.ListOptions) ([]*extent.Tag, error) { From 90dc2492d8c3f352dbf2cf53aea560668cdb2b6f Mon Sep 17 00:00:00 2001 From: Noah Lee Date: Mon, 27 May 2024 10:27:22 +0900 Subject: [PATCH 2/4] feat: Add GetDefaultBranch method to BranchSCM interface --- internal/server/api/v1/repos/branch_get.go | 19 +++++++++++++++++++ internal/server/api/v1/repos/interface.go | 1 + .../server/api/v1/repos/mock/interactor.go | 15 +++++++++++++++ internal/server/router.go | 1 + 4 files changed, 36 insertions(+) diff --git a/internal/server/api/v1/repos/branch_get.go b/internal/server/api/v1/repos/branch_get.go index 664b48c3..c2155f63 100644 --- a/internal/server/api/v1/repos/branch_get.go +++ b/internal/server/api/v1/repos/branch_get.go @@ -32,3 +32,22 @@ func (s *BranchAPI) Get(c *gin.Context) { gb.Response(c, http.StatusOK, b) } + +func (s *BranchAPI) GetDefault(c *gin.Context) { + ctx := c.Request.Context() + + uv, _ := c.Get(gb.KeyUser) + u := uv.(*ent.User) + + rv, _ := c.Get(KeyRepo) + repo := rv.(*ent.Repo) + + b, err := s.i.GetDefaultBranch(ctx, u, repo) + if err != nil { + s.log.Check(gb.GetZapLogLevel(err), "Failed to get the branch.").Write(zap.Error(err)) + gb.ResponseWithError(c, err) + return + } + + gb.Response(c, http.StatusOK, b) +} diff --git a/internal/server/api/v1/repos/interface.go b/internal/server/api/v1/repos/interface.go index fbf86191..26f43b9d 100644 --- a/internal/server/api/v1/repos/interface.go +++ b/internal/server/api/v1/repos/interface.go @@ -55,6 +55,7 @@ type ( ListBranches(ctx context.Context, u *ent.User, r *ent.Repo, opt *i.ListOptions) ([]*extent.Branch, error) GetBranch(ctx context.Context, u *ent.User, r *ent.Repo, branch string) (*extent.Branch, error) + GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) ListTags(ctx context.Context, u *ent.User, r *ent.Repo, opt *i.ListOptions) ([]*extent.Tag, error) GetTag(ctx context.Context, u *ent.User, r *ent.Repo, tag string) (*extent.Tag, error) diff --git a/internal/server/api/v1/repos/mock/interactor.go b/internal/server/api/v1/repos/mock/interactor.go index 142e711d..95e2b4b4 100644 --- a/internal/server/api/v1/repos/mock/interactor.go +++ b/internal/server/api/v1/repos/mock/interactor.go @@ -337,6 +337,21 @@ func (mr *MockInteractorMockRecorder) GetConfig(ctx, u, r interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfig", reflect.TypeOf((*MockInteractor)(nil).GetConfig), ctx, u, r) } +// GetDefaultBranch mocks base method. +func (m *MockInteractor) GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDefaultBranch", ctx, u, r) + ret0, _ := ret[0].(*extent.Branch) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDefaultBranch indicates an expected call of GetDefaultBranch. +func (mr *MockInteractorMockRecorder) GetDefaultBranch(ctx, u, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultBranch", reflect.TypeOf((*MockInteractor)(nil).GetDefaultBranch), ctx, u, r) +} + // GetEvaluatedConfig mocks base method. func (m *MockInteractor) GetEvaluatedConfig(ctx context.Context, u *ent.User, r *ent.Repo, v *extent.EvalValues) (*extent.Config, error) { m.ctrl.T.Helper() diff --git a/internal/server/router.go b/internal/server/router.go index dcd06729..bf58d9b9 100644 --- a/internal/server/router.go +++ b/internal/server/router.go @@ -133,6 +133,7 @@ func NewRouter(c *RouterConfig) *gin.Engine { repov1.GET("/:namespace/:name/commits/:sha/statuses", rm.RepoReadPerm(), api.Commits.ListStatuses) repov1.GET("/:namespace/:name/branches", rm.RepoReadPerm(), api.Branch.List) repov1.GET("/:namespace/:name/branches/:branch", rm.RepoReadPerm(), api.Branch.Get) + repov1.GET("/:namespace/:name/default-branch", rm.RepoReadPerm(), api.Branch.GetDefault) repov1.GET("/:namespace/:name/tags", rm.RepoReadPerm(), api.Tag.List) repov1.GET("/:namespace/:name/tags/:tag", rm.RepoReadPerm(), api.Tag.Get) repov1.GET("/:namespace/:name/deployments", rm.RepoReadPerm(), api.Deployment.List) From f52e994613e875bf4b817dcd796685122d0572fa Mon Sep 17 00:00:00 2001 From: Noah Lee Date: Mon, 27 May 2024 10:27:39 +0900 Subject: [PATCH 3/4] feat: Add getDefaultBranch method to branch API --- ui/src/apis/branch.ts | 24 ++++++++++++++++++++++++ ui/src/apis/index.ts | 2 +- ui/src/redux/repoDeploy.tsx | 19 ++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/ui/src/apis/branch.ts b/ui/src/apis/branch.ts index 785f08d4..154a4b63 100644 --- a/ui/src/apis/branch.ts +++ b/ui/src/apis/branch.ts @@ -52,3 +52,27 @@ export const getBranch = async ( return ret; }; + +export const getDefaultBranch = async ( + namespace: string, + name: string +): Promise => { + const response = await _fetch( + `${instance}/api/v1/repos/${namespace}/${name}/default-branch`, + { + headers, + credentials: 'same-origin', + } + ); + + if (response.status === StatusCodes.NOT_FOUND) { + const message = await response.json().then((data) => data.message); + throw new HttpNotFoundError(message); + } + + const ret: Branch = await response + .json() + .then((b: any) => mapDataToBranch(b)); + + return ret; +}; diff --git a/ui/src/apis/index.ts b/ui/src/apis/index.ts index 328743b8..ff710e30 100644 --- a/ui/src/apis/index.ts +++ b/ui/src/apis/index.ts @@ -20,7 +20,7 @@ export { } from './deployment'; export { getConfig } from './config'; export { listCommits, getCommit, listStatuses } from './commit'; -export { listBranches, getBranch } from './branch'; +export { listBranches, getBranch, getDefaultBranch } from './branch'; export { listTags, getTag } from './tag'; export { listUsers, updateUser, deleteUser, getMe, getRateLimit } from './user'; export { checkSlack } from './chat'; diff --git a/ui/src/redux/repoDeploy.tsx b/ui/src/redux/repoDeploy.tsx index c04e2d22..58fe7248 100644 --- a/ui/src/redux/repoDeploy.tsx +++ b/ui/src/redux/repoDeploy.tsx @@ -23,6 +23,7 @@ import { listDeployments, listBranches, getBranch, + getDefaultBranch, listCommits, getCommit, listStatuses, @@ -121,7 +122,23 @@ export const fetchBranches = createAsyncThunk< const { namespace, name } = getState().repoDeploy; const branches = await listBranches(namespace, name, firstPage, perPage); - return branches; + + const defaultBranch = await getDefaultBranch(namespace, name); + + // Add the default branch, and remove the duplicated one. + branches.unshift(defaultBranch); + + const reduced = branches.reduce((acc, cur) => { + if (acc.findIndex((b) => b.name === cur.name) === -1) { + acc.push(cur); + } + + return acc; + }, [] as Branch[]); + + console.log(reduced); + + return reduced; }); export const checkBranch = createAsyncThunk< From 78f42b50f0cdd36e17bd5e18c9c25ea775dbfcaf Mon Sep 17 00:00:00 2001 From: Noah Lee Date: Mon, 27 May 2024 10:31:12 +0900 Subject: [PATCH 4/4] feat: Add GetDefaultBranch method to BranchSCM interface --- internal/interactor/mock/pkg.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/internal/interactor/mock/pkg.go b/internal/interactor/mock/pkg.go index ddef757b..f708cb56 100644 --- a/internal/interactor/mock/pkg.go +++ b/internal/interactor/mock/pkg.go @@ -1263,6 +1263,21 @@ func (mr *MockSCMMockRecorder) GetConfigRedirectURL(ctx, u, r interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigRedirectURL", reflect.TypeOf((*MockSCM)(nil).GetConfigRedirectURL), ctx, u, r) } +// GetDefaultBranch mocks base method. +func (m *MockSCM) GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDefaultBranch", ctx, u, r) + ret0, _ := ret[0].(*extent.Branch) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDefaultBranch indicates an expected call of GetDefaultBranch. +func (mr *MockSCMMockRecorder) GetDefaultBranch(ctx, u, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultBranch", reflect.TypeOf((*MockSCM)(nil).GetDefaultBranch), ctx, u, r) +} + // GetNewConfigRedirectURL mocks base method. func (m *MockSCM) GetNewConfigRedirectURL(ctx context.Context, u *ent.User, r *ent.Repo) (string, error) { m.ctrl.T.Helper() @@ -1573,6 +1588,21 @@ func (mr *MockBranchSCMMockRecorder) GetBranch(ctx, u, r, branch interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBranch", reflect.TypeOf((*MockBranchSCM)(nil).GetBranch), ctx, u, r, branch) } +// GetDefaultBranch mocks base method. +func (m *MockBranchSCM) GetDefaultBranch(ctx context.Context, u *ent.User, r *ent.Repo) (*extent.Branch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDefaultBranch", ctx, u, r) + ret0, _ := ret[0].(*extent.Branch) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDefaultBranch indicates an expected call of GetDefaultBranch. +func (mr *MockBranchSCMMockRecorder) GetDefaultBranch(ctx, u, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultBranch", reflect.TypeOf((*MockBranchSCM)(nil).GetDefaultBranch), ctx, u, r) +} + // ListBranches mocks base method. func (m *MockBranchSCM) ListBranches(ctx context.Context, u *ent.User, r *ent.Repo, opt *interactor.ListOptions) ([]*extent.Branch, error) { m.ctrl.T.Helper()