endpoint to paginate through all repos

This commit is contained in:
Brad Rydzewski 2019-10-02 11:29:57 -07:00
parent 4596e510f6
commit cbfd342333
6 changed files with 152 additions and 46 deletions

View file

@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- added nsswitch to docker images
- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980).
- endpoint to list all repositories in the database, by [@bradrydzewski](https://github.com/bradrydzewski). [#2785](https://github.com/drone/drone/issues/2785).
## [1.5.1] - 2019-09-30
### Added

View file

@ -82,6 +82,10 @@ type (
// the datastore with incomplete builds.
ListIncomplete(context.Context) ([]*Repository, error)
// ListAll returns a paginated list of all repositories
// stored in the database, including disabled repositories.
ListAll(ctx context.Context, limit, offset int) ([]*Repository, error)
// Find returns a repository from the datastore.
Find(context.Context, int64) (*Repository, error)

View file

@ -154,67 +154,72 @@ func (s Server) Handler() http.Handler {
cors := cors.New(corsOpts)
r.Use(cors.Handler)
r.Route("/repos/{owner}/{name}", func(r chi.Router) {
r.Use(acl.InjectRepository(s.Repoz, s.Repos, s.Perms))
r.Use(acl.CheckReadAccess())
r.Get("/", repos.HandleFind())
r.Route("/repos", func(r chi.Router) {
r.With(
acl.CheckAdminAccess(),
).Patch("/", repos.HandleUpdate(s.Repos))
r.With(
acl.CheckAdminAccess(),
).Post("/", repos.HandleEnable(s.Hooks, s.Repos, s.Webhook))
r.With(
acl.CheckAdminAccess(),
).Delete("/", repos.HandleDisable(s.Repos, s.Webhook))
r.With(
acl.CheckAdminAccess(),
).Post("/chown", repos.HandleChown(s.Repos))
r.With(
acl.CheckAdminAccess(),
).Post("/repair", repos.HandleRepair(s.Hooks, s.Repoz, s.Repos, s.Users, s.System.Link))
).Get("/", repos.HandleAll(s.Repos))
r.Route("/builds", func(r chi.Router) {
r.Get("/", builds.HandleList(s.Repos, s.Builds))
r.With(acl.CheckWriteAccess()).Post("/", builds.HandleCreate(s.Repos, s.Commits, s.Triggerer))
r.Get("/latest", builds.HandleLast(s.Repos, s.Builds, s.Stages))
r.Get("/{number}", builds.HandleFind(s.Repos, s.Builds, s.Stages))
r.Get("/{number}/logs/{stage}/{step}", logs.HandleFind(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs))
r.With(
acl.CheckWriteAccess(),
).Post("/{number}", builds.HandleRetry(s.Repos, s.Builds, s.Triggerer))
r.With(
acl.CheckWriteAccess(),
).Delete("/{number}", builds.HandleCancel(s.Users, s.Repos, s.Builds, s.Stages, s.Steps, s.Status, s.Scheduler, s.Webhook))
r.With(
acl.CheckWriteAccess(),
).Post("/{number}/promote", builds.HandlePromote(s.Repos, s.Builds, s.Triggerer))
r.Route("/{owner}/{name}", func(r chi.Router) {
r.Use(acl.InjectRepository(s.Repoz, s.Repos, s.Perms))
r.Use(acl.CheckReadAccess())
r.Get("/", repos.HandleFind())
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/rollback", builds.HandleRollback(s.Repos, s.Builds, s.Triggerer))
).Patch("/", repos.HandleUpdate(s.Repos))
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/decline/{stage}", stages.HandleDecline(s.Repos, s.Builds, s.Stages))
).Post("/", repos.HandleEnable(s.Hooks, s.Repos, s.Webhook))
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/approve/{stage}", stages.HandleApprove(s.Repos, s.Builds, s.Stages, s.Scheduler))
).Delete("/", repos.HandleDisable(s.Repos, s.Webhook))
r.With(
acl.CheckAdminAccess(),
).Delete("/{number}/logs/{stage}/{step}", logs.HandleDelete(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs))
).Post("/chown", repos.HandleChown(s.Repos))
r.With(
acl.CheckAdminAccess(),
).Delete("/", builds.HandlePurge(s.Repos, s.Builds))
).Post("/repair", repos.HandleRepair(s.Hooks, s.Repoz, s.Repos, s.Users, s.System.Link))
r.Route("/builds", func(r chi.Router) {
r.Get("/", builds.HandleList(s.Repos, s.Builds))
r.With(acl.CheckWriteAccess()).Post("/", builds.HandleCreate(s.Repos, s.Commits, s.Triggerer))
r.Get("/latest", builds.HandleLast(s.Repos, s.Builds, s.Stages))
r.Get("/{number}", builds.HandleFind(s.Repos, s.Builds, s.Stages))
r.Get("/{number}/logs/{stage}/{step}", logs.HandleFind(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs))
r.With(
acl.CheckWriteAccess(),
).Post("/{number}", builds.HandleRetry(s.Repos, s.Builds, s.Triggerer))
r.With(
acl.CheckWriteAccess(),
).Delete("/{number}", builds.HandleCancel(s.Users, s.Repos, s.Builds, s.Stages, s.Steps, s.Status, s.Scheduler, s.Webhook))
r.With(
acl.CheckWriteAccess(),
).Post("/{number}/promote", builds.HandlePromote(s.Repos, s.Builds, s.Triggerer))
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/rollback", builds.HandleRollback(s.Repos, s.Builds, s.Triggerer))
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/decline/{stage}", stages.HandleDecline(s.Repos, s.Builds, s.Stages))
r.With(
acl.CheckAdminAccess(),
).Post("/{number}/approve/{stage}", stages.HandleApprove(s.Repos, s.Builds, s.Stages, s.Scheduler))
r.With(
acl.CheckAdminAccess(),
).Delete("/{number}/logs/{stage}/{step}", logs.HandleDelete(s.Repos, s.Builds, s.Stages, s.Steps, s.Logs))
r.With(
acl.CheckAdminAccess(),
).Delete("/", builds.HandlePurge(s.Repos, s.Builds))
})
})
r.Route("/secrets", func(r chi.Router) {

55
handler/api/repos/all.go Normal file
View file

@ -0,0 +1,55 @@
// Copyright 2019 Drone IO, Inc.
//
// 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 repos
import (
"net/http"
"strconv"
"github.com/drone/drone/core"
"github.com/drone/drone/handler/api/render"
"github.com/drone/drone/logger"
)
// HandleAll returns an http.HandlerFunc that processes http
// requests to list all repositories in the database.
func HandleAll(repos core.RepositoryStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var (
page = r.FormValue("page")
perPage = r.FormValue("per_page")
)
offset, _ := strconv.Atoi(page)
limit, _ := strconv.Atoi(perPage)
if limit < 1 { // || limit > 100
limit = 25
}
switch offset {
case 0, 1:
offset = 0
default:
offset = (offset - 1) * limit
}
repo, err := repos.ListAll(r.Context(), limit, offset)
if err != nil {
render.InternalError(w, err)
logger.FromRequest(r).
WithError(err).
Debugln("api: cannot list repositories")
} else {
render.JSON(w, repo, 200)
}
}
}

View file

@ -1768,6 +1768,21 @@ func (mr *MockRepositoryStoreMockRecorder) List(arg0, arg1 interface{}) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepositoryStore)(nil).List), arg0, arg1)
}
// ListAll mocks base method
func (m *MockRepositoryStore) ListAll(arg0 context.Context, arg1, arg2 int) ([]*core.Repository, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListAll", arg0, arg1, arg2)
ret0, _ := ret[0].([]*core.Repository)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListAll indicates an expected call of ListAll
func (mr *MockRepositoryStoreMockRecorder) ListAll(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAll", reflect.TypeOf((*MockRepositoryStore)(nil).ListAll), arg0, arg1, arg2)
}
// ListIncomplete mocks base method
func (m *MockRepositoryStore) ListIncomplete(arg0 context.Context) ([]*core.Repository, error) {
m.ctrl.T.Helper()

View file

@ -104,6 +104,27 @@ func (s *repoStore) ListIncomplete(ctx context.Context) ([]*core.Repository, err
return out, err
}
func (s *repoStore) ListAll(ctx context.Context, limit, offset int) ([]*core.Repository, error) {
var out []*core.Repository
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
params := map[string]interface{}{
"limit": limit,
"offset": offset,
}
query, args, err := binder.BindNamed(queryAll, params)
if err != nil {
return err
}
rows, err := queryer.Query(query, args...)
if err != nil {
return err
}
out, err = scanRows(rows)
return err
})
return out, err
}
func (s *repoStore) Find(ctx context.Context, id int64) (*core.Repository, error) {
out := &core.Repository{ID: id}
err := s.db.View(func(queryer db.Queryer, binder db.Binder) error {
@ -329,6 +350,11 @@ WHERE perms.perm_user_id = :user_id
ORDER BY repo_slug ASC
`
const queryAll = queryCols + `
FROM repos
LIMIT :limit OFFSET :offset
`
const stmtDelete = `
DELETE FROM repos WHERE repo_id = :repo_id
`