card api and db work to support new cards feature
This commit is contained in:
parent
947bcbe92a
commit
85fd11c421
31 changed files with 1976 additions and 5 deletions
|
@ -79,6 +79,7 @@ tasks:
|
|||
- cmd: go test -count=1 github.com/drone/drone/store/batch
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/batch2
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/build
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/card
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/cron
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/logs
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/perm
|
||||
|
@ -113,6 +114,7 @@ tasks:
|
|||
- cmd: go test -count=1 github.com/drone/drone/store/batch
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/batch2
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/build
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/card
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/cron
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/logs
|
||||
- cmd: go test -count=1 github.com/drone/drone/store/perm
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/drone/drone/store/batch"
|
||||
"github.com/drone/drone/store/batch2"
|
||||
"github.com/drone/drone/store/build"
|
||||
"github.com/drone/drone/store/card"
|
||||
"github.com/drone/drone/store/cron"
|
||||
"github.com/drone/drone/store/logs"
|
||||
"github.com/drone/drone/store/perm"
|
||||
|
@ -50,6 +51,7 @@ var storeSet = wire.NewSet(
|
|||
provideBatchStore,
|
||||
// batch.New,
|
||||
cron.New,
|
||||
card.New,
|
||||
perm.New,
|
||||
secret.New,
|
||||
global.New,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run github.com/google/wire/cmd/wire
|
||||
//go:generate wire
|
||||
//+build !wireinject
|
||||
|
||||
package main
|
||||
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/drone/drone/service/token"
|
||||
"github.com/drone/drone/service/transfer"
|
||||
"github.com/drone/drone/service/user"
|
||||
"github.com/drone/drone/store/card"
|
||||
"github.com/drone/drone/store/cron"
|
||||
"github.com/drone/drone/store/perm"
|
||||
"github.com/drone/drone/store/secret"
|
||||
|
@ -86,6 +87,7 @@ func InitializeApplication(config2 config.Config) (application, error) {
|
|||
secretService := provideSecretPlugin(config2)
|
||||
registryService := provideRegistryPlugin(config2)
|
||||
runner := provideRunner(buildManager, secretService, registryService, config2)
|
||||
cardStore := card.New(db)
|
||||
hookService := provideHookService(client, renewer, config2)
|
||||
licenseService := license.NewService(userStore, repositoryStore, buildStore, coreLicense)
|
||||
organizationService := provideOrgService(client, renewer)
|
||||
|
@ -99,7 +101,7 @@ func InitializeApplication(config2 config.Config) (application, error) {
|
|||
syncer := provideSyncer(repositoryService, repositoryStore, userStore, batcher, config2)
|
||||
transferer := transfer.New(repositoryStore, permStore)
|
||||
userService := user.New(client, renewer)
|
||||
server := api.New(buildStore, commitService, cronStore, corePubsub, globalSecretStore, hookService, logStore, coreLicense, licenseService, organizationService, permStore, repositoryStore, repositoryService, scheduler, secretStore, stageStore, stepStore, statusService, session, logStream, syncer, system, templateStore, transferer, triggerer, userStore, userService, webhookSender)
|
||||
server := api.New(buildStore, commitService, cardStore, cronStore, corePubsub, globalSecretStore, hookService, logStore, coreLicense, licenseService, organizationService, permStore, repositoryStore, repositoryService, scheduler, secretStore, stageStore, stepStore, statusService, session, logStream, syncer, system, templateStore, transferer, triggerer, userStore, userService, webhookSender)
|
||||
admissionService := provideAdmissionPlugin(client, organizationService, userService, config2)
|
||||
hookParser := parser.New(client)
|
||||
coreLinker := linker.New(client)
|
||||
|
|
89
core/card.go
Normal file
89
core/card.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// 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 core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errCardStepInvalid = errors.New("No Step ID Provided")
|
||||
errCardBuildInvalid = errors.New("No Build ID Provided")
|
||||
errCardSchemaInvalid = errors.New("No Card Schema Has Been Provided")
|
||||
errCardDataInvalid = errors.New("No Card Data Has Been Provided")
|
||||
)
|
||||
|
||||
type Card struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Build int64 `json:"build,omitempty"`
|
||||
Stage int64 `json:"stage,omitempty"`
|
||||
Step int64 `json:"step,omitempty"`
|
||||
Schema string `json:"schema,omitempty"`
|
||||
}
|
||||
|
||||
type CreateCard struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Build int64 `json:"build,omitempty"`
|
||||
Stage int64 `json:"stage,omitempty"`
|
||||
Step int64 `json:"step,omitempty"`
|
||||
Schema string `json:"schema,omitempty"`
|
||||
Data []byte `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type CardData struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Data []byte `json:"card_data"`
|
||||
}
|
||||
|
||||
// CardStore manages repository cards.
|
||||
type CardStore interface {
|
||||
FindCardByBuild(ctx context.Context, build int64) ([]*Card, error)
|
||||
FindCard(ctx context.Context, step int64) (*Card, error)
|
||||
FindCardData(ctx context.Context, id int64) (io.ReadCloser, error)
|
||||
CreateCard(ctx context.Context, card *CreateCard) error
|
||||
DeleteCard(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
// Validate validates the required fields and formats.
|
||||
func (c *Card) Validate() error {
|
||||
switch {
|
||||
case c.Step == 0:
|
||||
return errCardStepInvalid
|
||||
case c.Build == 0:
|
||||
return errCardBuildInvalid
|
||||
case len(c.Schema) == 0:
|
||||
return errCardSchemaInvalid
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CreateCard) Validate() error {
|
||||
switch {
|
||||
case c.Step == 0:
|
||||
return errCardStepInvalid
|
||||
case c.Build == 0:
|
||||
return errCardBuildInvalid
|
||||
case len(c.Schema) == 0:
|
||||
return errCardSchemaInvalid
|
||||
case len(c.Data) == 0:
|
||||
return errCardDataInvalid
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
2
go.mod
2
go.mod
|
@ -10,7 +10,7 @@ require (
|
|||
github.com/aws/aws-sdk-go v1.37.3
|
||||
github.com/codegangsta/negroni v1.0.0 // indirect
|
||||
github.com/coreos/go-semver v0.2.0
|
||||
github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866
|
||||
github.com/dchest/authcookie v0.0.0-20190824115100-f900d2294c8e
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9
|
||||
github.com/drone/drone-go v1.4.1-0.20201109202657-b9e58bbbcf27
|
||||
github.com/drone/drone-runtime v1.1.1-0.20200623162453-61e33e2cab5d
|
||||
|
|
2
go.sum
2
go.sum
|
@ -58,6 +58,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866 h1:98WJ4YCdjmB7uyrdT3P4A2Oa1hiRPKoa/0zInG6UnfQ=
|
||||
github.com/dchest/authcookie v0.0.0-20120917135355-fbdef6e99866/go.mod h1:x7AK2h2QzaXVEFi1tbMYMDuvHcCEr1QdMDrg3hkW24Q=
|
||||
github.com/dchest/authcookie v0.0.0-20190824115100-f900d2294c8e h1:xizeG5ksKSdyNaom2//2Bow4hLWqXkCql36nrL9iEUI=
|
||||
github.com/dchest/authcookie v0.0.0-20190824115100-f900d2294c8e/go.mod h1:x7AK2h2QzaXVEFi1tbMYMDuvHcCEr1QdMDrg3hkW24Q=
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 h1:74lLNRzvsdIlkTgfDSMuaPjBr4cf6k7pwQQANm/yLKU=
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
|
|
|
@ -15,7 +15,10 @@
|
|||
package acl
|
||||
|
||||
import (
|
||||
"github.com/dchest/authcookie"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
|
@ -48,6 +51,36 @@ func CheckAdminAccess() func(http.Handler) http.Handler {
|
|||
return CheckAccess(true, true, true)
|
||||
}
|
||||
|
||||
func CheckInternalAccess() func(http.Handler) http.Handler {
|
||||
return checkIAccess()
|
||||
}
|
||||
|
||||
// CheckIAccess returns an http.Handler middleware that authorizes only
|
||||
// allows plugins to communicate with the server
|
||||
func checkIAccess() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
owner = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
log := logger.FromRequest(r).
|
||||
WithField("namespace", owner).
|
||||
WithField("name", name)
|
||||
|
||||
secret := []byte(os.Getenv("DRONE_INTERNAL_AUTH_SECRET"))
|
||||
login := authcookie.Login(extractToken(r), secret)
|
||||
if login != "" {
|
||||
log.Debug("api: access granted")
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
render.Unauthorized(w, errors.ErrUnauthorized)
|
||||
log.Debugln("api: authentication required for access")
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CheckAccess returns an http.Handler middleware that authorizes only
|
||||
// authenticated users with the required read, write or admin access
|
||||
// permissions to the requested repository resource.
|
||||
|
@ -144,3 +177,11 @@ func CheckAccess(read, write, admin bool) func(http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func extractToken(r *http.Request) string {
|
||||
bearer := r.Header.Get("Authorization")
|
||||
if bearer == "" {
|
||||
bearer = r.FormValue("access_token")
|
||||
}
|
||||
return strings.TrimPrefix(bearer, "Bearer ")
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/drone/drone/handler/api/auth"
|
||||
"github.com/drone/drone/handler/api/badge"
|
||||
globalbuilds "github.com/drone/drone/handler/api/builds"
|
||||
"github.com/drone/drone/handler/api/card"
|
||||
"github.com/drone/drone/handler/api/ccmenu"
|
||||
"github.com/drone/drone/handler/api/events"
|
||||
"github.com/drone/drone/handler/api/queue"
|
||||
|
@ -63,6 +64,7 @@ var corsOpts = cors.Options{
|
|||
func New(
|
||||
builds core.BuildStore,
|
||||
commits core.CommitService,
|
||||
card core.CardStore,
|
||||
cron core.CronStore,
|
||||
events core.Pubsub,
|
||||
globals core.GlobalSecretStore,
|
||||
|
@ -92,6 +94,7 @@ func New(
|
|||
) Server {
|
||||
return Server{
|
||||
Builds: builds,
|
||||
Card: card,
|
||||
Cron: cron,
|
||||
Commits: commits,
|
||||
Events: events,
|
||||
|
@ -125,6 +128,7 @@ func New(
|
|||
// Server is a http.Handler which exposes drone functionality over HTTP.
|
||||
type Server struct {
|
||||
Builds core.BuildStore
|
||||
Card core.CardStore
|
||||
Cron core.CronStore
|
||||
Commits core.CommitService
|
||||
Events core.Pubsub
|
||||
|
@ -166,6 +170,11 @@ func (s Server) Handler() http.Handler {
|
|||
cors := cors.New(corsOpts)
|
||||
r.Use(cors.Handler)
|
||||
|
||||
r.Route("/internal/{owner}/{name}", func(r chi.Router) {
|
||||
r.With(
|
||||
acl.CheckInternalAccess(),
|
||||
).Post("/cards/{build}/{stage}/{step}", card.HandleCreate(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
})
|
||||
r.Route("/repos", func(r chi.Router) {
|
||||
// temporary workaround to enable private mode
|
||||
// for the drone server.
|
||||
|
@ -285,6 +294,16 @@ func (s Server) Handler() http.Handler {
|
|||
acl.CheckAdminAccess(),
|
||||
).Delete("/{member}", collabs.HandleDelete(s.Users, s.Repos, s.Perms))
|
||||
})
|
||||
|
||||
r.Route("/cards", func(r chi.Router) {
|
||||
r.Get("/{build}", card.HandleFindAll(s.Builds, s.Card, s.Repos))
|
||||
r.Get("/{build}/{stage}/{step}", card.HandleFind(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
r.Get("/{build}/{stage}/{step}/json", card.HandleFindData(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
r.With(
|
||||
acl.AuthorizeAdmin,
|
||||
acl.CheckAdminAccess(),
|
||||
).Delete("/{build}/{stage}/{step}", card.HandleDelete(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
108
handler/api/card/create.go
Normal file
108
handler/api/card/create.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
type cardInput struct {
|
||||
Schema string `json:"schema"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// HandleCreate returns an http.HandlerFunc that processes http
|
||||
// requests to create a new card.
|
||||
func HandleCreate(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
|
||||
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stepNumber, err := strconv.Atoi(chi.URLParam(r, "step"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
in := new(cardInput)
|
||||
err = json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
c := &core.CreateCard{
|
||||
Build: build.ID,
|
||||
Stage: stage.ID,
|
||||
Step: step.ID,
|
||||
Schema: in.Schema,
|
||||
Data: in.Data,
|
||||
}
|
||||
|
||||
err = c.Validate()
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = cardStore.CreateCard(r.Context(), c)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, c, 200)
|
||||
}
|
||||
}
|
224
handler/api/card/create_test.go
Normal file
224
handler/api/card/create_test.go
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
var (
|
||||
dummyRepo = &core.Repository{
|
||||
ID: 1,
|
||||
UserID: 1,
|
||||
Slug: "octocat/hello-world",
|
||||
}
|
||||
dummyBuild = &core.Build{
|
||||
ID: 1,
|
||||
RepoID: 1,
|
||||
Number: 1,
|
||||
}
|
||||
dummyStage = &core.Stage{
|
||||
ID: 1,
|
||||
BuildID: 1,
|
||||
}
|
||||
dummyStep = &core.Step{
|
||||
ID: 1,
|
||||
StageID: 1,
|
||||
}
|
||||
dummyCreateCard = &core.CreateCard{
|
||||
Schema: "https://myschema.com",
|
||||
Data: "{\"type\": \"AdaptiveCard\"}",
|
||||
}
|
||||
dummyCard = &core.Card{
|
||||
Id: 1,
|
||||
Build: 1,
|
||||
Stage: 1,
|
||||
Step: 1,
|
||||
Schema: "https://myschema.com",
|
||||
}
|
||||
dummyCardNoData = &core.CreateCard{
|
||||
Schema: "https://myschema.com",
|
||||
}
|
||||
dummyCardList = []*core.Card{
|
||||
dummyCard,
|
||||
}
|
||||
dummyCardData = ioutil.NopCloser(
|
||||
bytes.NewBuffer([]byte(dummyCreateCard.Data)),
|
||||
)
|
||||
)
|
||||
|
||||
func TestHandleCreate(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().CreateCard(gomock.Any(), gomock.Any()).Return(nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleCreate(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusOK; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCreate_ValidationErrorData(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCardNoData)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleCreate(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusBadRequest; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := &errors.Error{}, &errors.Error{Message: "No Card Data Has Been Provided"}
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCreate_BadRequest(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", nil)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleCreate(nil, nil, nil, nil, nil).ServeHTTP(w, r)
|
||||
got, want := &errors.Error{}, &errors.Error{Message: "EOF"}
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCreate_CreateError(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().CreateCard(gomock.Any(), gomock.Any()).Return(errors.ErrNotFound)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleCreate(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusInternalServerError; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(errors.Error), errors.ErrNotFound
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
86
handler/api/card/delete.go
Normal file
86
handler/api/card/delete.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleDelete returns an http.HandlerFunc that processes http
|
||||
// requests to delete a card.
|
||||
func HandleDelete(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
|
||||
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stepNumber, err := strconv.Atoi(chi.URLParam(r, "step"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
card, err := cardStore.FindCard(r.Context(), step.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
err = cardStore.DeleteCard(r.Context(), card.Id)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
}
|
150
handler/api/card/delete_test.go
Normal file
150
handler/api/card/delete_test.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestHandleDelete(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().DeleteCard(gomock.Any(), dummyCard.Id).Return(nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("DELETE", "/", nil)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusNoContent; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDelete_CardNotFound(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(nil, errors.ErrNotFound)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("DELETE", "/", nil)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusNotFound; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(errors.Error), errors.ErrNotFound
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDelete_DeleteError(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().DeleteCard(gomock.Any(), dummyCard.Id).Return(errors.ErrNotFound)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("DELETE", "/", nil)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleDelete(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusInternalServerError; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(errors.Error), errors.ErrNotFound
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
79
handler/api/card/find.go
Normal file
79
handler/api/card/find.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleFind returns an http.HandlerFunc that writes a json-encoded
|
||||
func HandleFind(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
|
||||
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stepNumber, err := strconv.Atoi(chi.URLParam(r, "step"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
card, err := cardStore.FindCard(r.Context(), step.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
render.JSON(w, card, 200)
|
||||
}
|
||||
}
|
55
handler/api/card/find_all.go
Normal file
55
handler/api/card/find_all.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleFindAll returns an http.HandlerFunc that writes a json-encoded
|
||||
func HandleFindAll(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
|
||||
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := cardStore.FindCardByBuild(r.Context(), build.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
render.JSON(w, list, 200)
|
||||
}
|
||||
}
|
97
handler/api/card/find_all_test.go
Normal file
97
handler/api/card/find_all_test.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestHandleFindAll(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCardByBuild(gomock.Any(), dummyBuild.ID).Return(dummyCardList, nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleFindAll(build, card, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusOK; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleFindAll_CardsNotFound(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCardByBuild(gomock.Any(), dummyBuild.ID).Return(nil, errors.ErrNotFound)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleFindAll(build, card, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusNotFound; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(errors.Error), errors.ErrNotFound
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
87
handler/api/card/find_data.go
Normal file
87
handler/api/card/find_data.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
// HandleFindData returns an http.HandlerFunc that writes a json-encoded
|
||||
func HandleFindData(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
namespace = chi.URLParam(r, "owner")
|
||||
name = chi.URLParam(r, "name")
|
||||
)
|
||||
|
||||
buildNumber, err := strconv.ParseInt(chi.URLParam(r, "build"), 10, 64)
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stageNumber, err := strconv.Atoi(chi.URLParam(r, "stage"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
stepNumber, err := strconv.Atoi(chi.URLParam(r, "step"))
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repoStore.FindName(r.Context(), namespace, name)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
build, err := buildStore.FindNumber(r.Context(), repo.ID, buildNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
stage, err := stageStore.FindNumber(r.Context(), build.ID, stageNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
step, err := stepStore.FindNumber(r.Context(), stage.ID, stepNumber)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
card, err := cardStore.FindCard(r.Context(), step.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
cardData, err := cardStore.FindCardData(r.Context(), card.Id)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
io.Copy(w, cardData)
|
||||
cardData.Close()
|
||||
}
|
||||
}
|
63
handler/api/card/find_data_test.go
Normal file
63
handler/api/card/find_data_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
func TestHandleFindCardData(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().FindCardData(gomock.Any(), dummyCard.Id).Return(dummyCardData, nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleFindData(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusOK; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
}
|
111
handler/api/card/find_test.go
Normal file
111
handler/api/card/find_test.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/handler/api/errors"
|
||||
"github.com/drone/drone/mock"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestHandleFind(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleFind(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusOK; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleFind_CardNotFound(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
repos := mock.NewMockRepositoryStore(controller)
|
||||
repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(dummyRepo, nil)
|
||||
|
||||
build := mock.NewMockBuildStore(controller)
|
||||
build.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyBuild, nil)
|
||||
|
||||
stage := mock.NewMockStageStore(controller)
|
||||
stage.EXPECT().FindNumber(gomock.Any(), dummyBuild.ID, gomock.Any()).Return(dummyStage, nil)
|
||||
|
||||
step := mock.NewMockStepStore(controller)
|
||||
step.EXPECT().FindNumber(gomock.Any(), dummyStage.ID, gomock.Any()).Return(dummyStep, nil)
|
||||
|
||||
card := mock.NewMockCardStore(controller)
|
||||
card.EXPECT().FindCard(gomock.Any(), dummyStep.ID).Return(nil, errors.ErrNotFound)
|
||||
|
||||
c := new(chi.Context)
|
||||
c.URLParams.Add("owner", "octocat")
|
||||
c.URLParams.Add("name", "hello-world")
|
||||
c.URLParams.Add("build", "1")
|
||||
c.URLParams.Add("stage", "1")
|
||||
c.URLParams.Add("step", "1")
|
||||
|
||||
in := new(bytes.Buffer)
|
||||
json.NewEncoder(in).Encode(dummyCreateCard)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", in)
|
||||
r = r.WithContext(
|
||||
context.WithValue(context.Background(), chi.RouteCtxKey, c),
|
||||
)
|
||||
|
||||
HandleFind(build, card, stage, step, repos).ServeHTTP(w, r)
|
||||
if got, want := w.Code, http.StatusNotFound; want != got {
|
||||
t.Errorf("Want response code %d, got %d", want, got)
|
||||
}
|
||||
|
||||
got, want := new(errors.Error), errors.ErrNotFound
|
||||
json.NewDecoder(w.Body).Decode(got)
|
||||
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||||
t.Errorf(diff)
|
||||
}
|
||||
}
|
76
handler/api/card/none.go
Normal file
76
handler/api/card/none.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// 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.
|
||||
|
||||
// +build oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/handler/api/render"
|
||||
)
|
||||
|
||||
var notImplemented = func(w http.ResponseWriter, r *http.Request) {
|
||||
render.NotImplemented(w, render.ErrNotImplemented)
|
||||
}
|
||||
|
||||
func HandleCreate(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return notImplemented
|
||||
}
|
||||
|
||||
func HandleDelete(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return notImplemented
|
||||
}
|
||||
|
||||
func HandleFind(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return notImplemented
|
||||
}
|
||||
|
||||
func HandleFindAll(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return notImplemented
|
||||
}
|
||||
|
||||
func HandleFindData(
|
||||
buildStore core.BuildStore,
|
||||
cardStore core.CardStore,
|
||||
stageStore core.StageStore,
|
||||
stepStore core.StepStore,
|
||||
repoStore core.RepositoryStore,
|
||||
) http.HandlerFunc {
|
||||
return notImplemented
|
||||
}
|
|
@ -6,4 +6,4 @@
|
|||
|
||||
package mock
|
||||
|
||||
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore
|
||||
//go:generate mockgen -package=mock -destination=mock_gen.go github.com/drone/drone/core Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore,CardStore
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/drone/drone/core (interfaces: Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore)
|
||||
// Source: github.com/drone/drone/core (interfaces: Pubsub,Canceler,ConvertService,ValidateService,NetrcService,Renewer,HookParser,UserService,RepositoryService,CommitService,StatusService,HookService,FileService,Batcher,BuildStore,CronStore,LogStore,PermStore,SecretStore,GlobalSecretStore,StageStore,StepStore,RepositoryStore,UserStore,Scheduler,Session,OrganizationService,SecretService,RegistryService,ConfigService,Transferer,Triggerer,Syncer,LogStream,WebhookSender,LicenseService,TemplateStore,CardStore)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
@ -2879,3 +2879,99 @@ func (mr *MockTemplateStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockTemplateStore)(nil).Update), arg0, arg1)
|
||||
}
|
||||
|
||||
// MockCardStore is a mock of CardStore interface.
|
||||
type MockCardStore struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockCardStoreMockRecorder
|
||||
}
|
||||
|
||||
// MockCardStoreMockRecorder is the mock recorder for MockCardStore.
|
||||
type MockCardStoreMockRecorder struct {
|
||||
mock *MockCardStore
|
||||
}
|
||||
|
||||
// NewMockCardStore creates a new mock instance.
|
||||
func NewMockCardStore(ctrl *gomock.Controller) *MockCardStore {
|
||||
mock := &MockCardStore{ctrl: ctrl}
|
||||
mock.recorder = &MockCardStoreMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockCardStore) EXPECT() *MockCardStoreMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CreateCard mocks base method.
|
||||
func (m *MockCardStore) CreateCard(arg0 context.Context, arg1 *core.CreateCard) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateCard", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateCard indicates an expected call of CreateCard.
|
||||
func (mr *MockCardStoreMockRecorder) CreateCard(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCard", reflect.TypeOf((*MockCardStore)(nil).CreateCard), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteCard mocks base method.
|
||||
func (m *MockCardStore) DeleteCard(arg0 context.Context, arg1 int64) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteCard", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteCard indicates an expected call of DeleteCard.
|
||||
func (mr *MockCardStoreMockRecorder) DeleteCard(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCard", reflect.TypeOf((*MockCardStore)(nil).DeleteCard), arg0, arg1)
|
||||
}
|
||||
|
||||
// FindCard mocks base method.
|
||||
func (m *MockCardStore) FindCard(arg0 context.Context, arg1 int64) (*core.Card, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindCard", arg0, arg1)
|
||||
ret0, _ := ret[0].(*core.Card)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindCard indicates an expected call of FindCard.
|
||||
func (mr *MockCardStoreMockRecorder) FindCard(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindCard", reflect.TypeOf((*MockCardStore)(nil).FindCard), arg0, arg1)
|
||||
}
|
||||
|
||||
// FindCardByBuild mocks base method.
|
||||
func (m *MockCardStore) FindCardByBuild(arg0 context.Context, arg1 int64) ([]*core.Card, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindCardByBuild", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*core.Card)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindCardByBuild indicates an expected call of FindCardByBuild.
|
||||
func (mr *MockCardStoreMockRecorder) FindCardByBuild(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindCardByBuild", reflect.TypeOf((*MockCardStore)(nil).FindCardByBuild), arg0, arg1)
|
||||
}
|
||||
|
||||
// FindCardData mocks base method.
|
||||
func (m *MockCardStore) FindCardData(arg0 context.Context, arg1 int64) (io.Reader, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindCardData", arg0, arg1)
|
||||
ret0, _ := ret[0].(io.Reader)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindCardData indicates an expected call of FindCardData.
|
||||
func (mr *MockCardStoreMockRecorder) FindCardData(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindCardData", reflect.TypeOf((*MockCardStore)(nil).FindCardData), arg0, arg1)
|
||||
}
|
||||
|
|
193
store/card/card.go
Normal file
193
store/card/card.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
)
|
||||
|
||||
// New returns a new card database store.
|
||||
func New(db *db.DB) core.CardStore {
|
||||
return &cardStore{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type cardStore struct {
|
||||
db *db.DB
|
||||
}
|
||||
|
||||
func (c *cardStore) FindCardByBuild(ctx context.Context, build int64) ([]*core.Card, error) {
|
||||
var out []*core.Card
|
||||
err := c.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"card_build": build,
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(queryByBuild, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := queryer.Query(stmt, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err = scanRows(rows)
|
||||
return err
|
||||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c cardStore) FindCard(ctx context.Context, step int64) (*core.Card, error) {
|
||||
out := &core.Card{Step: step}
|
||||
err := c.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
||||
params, err := toParams(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query, args, err := binder.BindNamed(queryByStep, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
row := queryer.QueryRow(query, args...)
|
||||
return scanRow(row, out)
|
||||
})
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (c cardStore) FindCardData(ctx context.Context, id int64) (io.ReadCloser, error) {
|
||||
out := &core.CardData{}
|
||||
err := c.db.View(func(queryer db.Queryer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"card_id": id,
|
||||
}
|
||||
query, args, err := binder.BindNamed(queryKey, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
row := queryer.QueryRow(query, args...)
|
||||
return scanRowCardDataOnly(row, out)
|
||||
})
|
||||
return ioutil.NopCloser(
|
||||
bytes.NewBuffer(out.Data),
|
||||
), err
|
||||
}
|
||||
|
||||
func (c cardStore) CreateCard(ctx context.Context, card *core.CreateCard) error {
|
||||
if c.db.Driver() == db.Postgres {
|
||||
return c.createPostgres(ctx, card)
|
||||
}
|
||||
return c.create(ctx, card)
|
||||
}
|
||||
|
||||
func (c *cardStore) create(ctx context.Context, card *core.CreateCard) error {
|
||||
return c.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params, err := toSaveCardParams(card)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtInsert, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := execer.Exec(stmt, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
card.Id, err = res.LastInsertId()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (c *cardStore) createPostgres(ctx context.Context, card *core.CreateCard) error {
|
||||
return c.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params, err := toSaveCardParams(card)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtInsertPostgres, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return execer.QueryRow(stmt, args...).Scan(&card.Id)
|
||||
})
|
||||
}
|
||||
|
||||
func (c cardStore) DeleteCard(ctx context.Context, id int64) error {
|
||||
return c.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
params := map[string]interface{}{
|
||||
"card_id": id,
|
||||
}
|
||||
stmt, args, err := binder.BindNamed(stmtDelete, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = execer.Exec(stmt, args...)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
const queryBase = `
|
||||
SELECT
|
||||
card_id
|
||||
,card_build
|
||||
,card_stage
|
||||
,card_step
|
||||
,card_schema
|
||||
`
|
||||
|
||||
const queryCardData = `
|
||||
SELECT
|
||||
card_id
|
||||
,card_data
|
||||
`
|
||||
|
||||
const queryByBuild = queryBase + `
|
||||
FROM cards
|
||||
WHERE card_build = :card_build
|
||||
`
|
||||
|
||||
const queryByStep = queryBase + `
|
||||
FROM cards
|
||||
WHERE card_step = :card_step
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
const queryKey = queryCardData + `
|
||||
FROM cards
|
||||
WHERE card_id = :card_id
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
const stmtInsert = `
|
||||
INSERT INTO cards (
|
||||
card_build
|
||||
,card_stage
|
||||
,card_step
|
||||
,card_schema
|
||||
,card_data
|
||||
) VALUES (
|
||||
:card_build
|
||||
,:card_stage
|
||||
,:card_step
|
||||
,:card_schema
|
||||
,:card_data
|
||||
)
|
||||
`
|
||||
|
||||
const stmtDelete = `
|
||||
DELETE FROM cards
|
||||
WHERE card_id = :card_id
|
||||
`
|
||||
|
||||
const stmtInsertPostgres = stmtInsert + `
|
||||
RETURNING card_id
|
||||
`
|
51
store/card/card_oss.go
Normal file
51
store/card/card_oss.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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.
|
||||
|
||||
// +build oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
)
|
||||
|
||||
func New(db *db.DB) core.CardStore {
|
||||
return new(noop)
|
||||
}
|
||||
|
||||
type noop struct{}
|
||||
|
||||
func (noop) FindCardByBuild(ctx context.Context, build int64) ([]*core.Card, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) FindCard(ctx context.Context, step int64) (*core.Card, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) FindCardData(ctx context.Context, id int64) (io.Reader, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) CreateCard(ctx context.Context, card *core.CreateCard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noop) DeleteCard(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
130
store/card/card_test.go
Normal file
130
store/card/card_test.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package card
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db/dbtest"
|
||||
)
|
||||
|
||||
var noContext = context.TODO()
|
||||
|
||||
func TestCard(t *testing.T) {
|
||||
conn, err := dbtest.Connect()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
dbtest.Reset(conn)
|
||||
dbtest.Disconnect(conn)
|
||||
}()
|
||||
|
||||
store := New(conn).(*cardStore)
|
||||
t.Run("Create", testCardCreate(store))
|
||||
}
|
||||
|
||||
func testCard(item *core.Card) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
if got, want := item.Schema, "https://myschema.com"; got != want {
|
||||
t.Errorf("Want card schema %q, got %q", want, got)
|
||||
}
|
||||
if got, want := item.Build, int64(1); got != want {
|
||||
t.Errorf("Want card build number %q, got %q", want, got)
|
||||
}
|
||||
if got, want := item.Stage, int64(2); got != want {
|
||||
t.Errorf("Want card stage number %q, got %q", want, got)
|
||||
}
|
||||
if got, want := item.Step, int64(3); got != want {
|
||||
t.Errorf("Want card step number %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCardCreate(store *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
item := &core.CreateCard{
|
||||
Id: 1,
|
||||
Build: 1,
|
||||
Stage: 2,
|
||||
Step: 3,
|
||||
Schema: "https://myschema.com",
|
||||
Data: "{\"type\": \"AdaptiveCard\"}",
|
||||
}
|
||||
err := store.CreateCard(noContext, item)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if item.Id == 0 {
|
||||
t.Errorf("Want card Id assigned, got %d", item.Id)
|
||||
}
|
||||
|
||||
t.Run("FindByBuild", testFindCardByBuild(store))
|
||||
t.Run("FindCard", testFindCard(store))
|
||||
t.Run("FindCardData", testFindCardData(store))
|
||||
t.Run("Delete", testCardDelete(store))
|
||||
}
|
||||
}
|
||||
|
||||
func testFindCardByBuild(card *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
item, err := card.FindCardByBuild(noContext, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Run("Fields", testCard(item[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFindCard(card *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
item, err := card.FindCard(noContext, 3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Run("Fields", testCard(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFindCardData(card *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
r, err := card.FindCardData(noContext, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if got, want := string(data), "{\"type\": \"AdaptiveCard\"}"; got != want {
|
||||
t.Errorf("Want card data output stream %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCardDelete(store *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
card, err := store.FindCard(noContext, 3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = store.DeleteCard(noContext, card.Id)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
_, err = store.FindCard(noContext, card.Step)
|
||||
if got, want := sql.ErrNoRows, err; got != want {
|
||||
t.Errorf("Want sql.ErrNoRows, got %v", got)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
79
store/card/scan.go
Normal file
79
store/card/scan.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Drone Non-Commercial License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// +build !oss
|
||||
|
||||
package card
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/store/shared/db"
|
||||
)
|
||||
|
||||
// helper function converts the card structure to a set
|
||||
// of named query parameters.
|
||||
func toParams(card *core.Card) (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"card_id": card.Id,
|
||||
"card_build": card.Build,
|
||||
"card_stage": card.Stage,
|
||||
"card_step": card.Step,
|
||||
"card_schema": card.Schema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// helper function converts the card structure to a set
|
||||
// of named query parameters.
|
||||
func toSaveCardParams(card *core.CreateCard) (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"card_id": card.Id,
|
||||
"card_build": card.Build,
|
||||
"card_stage": card.Stage,
|
||||
"card_step": card.Step,
|
||||
"card_schema": card.Schema,
|
||||
"card_data": card.Data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// helper function scans the sql.Row and copies the column
|
||||
// values to the destination object.
|
||||
func scanRow(scanner db.Scanner, dst *core.Card) error {
|
||||
err := scanner.Scan(
|
||||
&dst.Id,
|
||||
&dst.Build,
|
||||
&dst.Stage,
|
||||
&dst.Step,
|
||||
&dst.Schema,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanRowCardDataOnly(scanner db.Scanner, dst *core.CardData) error {
|
||||
return scanner.Scan(
|
||||
&dst.Id,
|
||||
&dst.Data,
|
||||
)
|
||||
}
|
||||
|
||||
// helper function scans the sql.Row and copies the column
|
||||
// values to the destination object.
|
||||
func scanRows(rows *sql.Rows) ([]*core.Card, error) {
|
||||
defer rows.Close()
|
||||
|
||||
card := []*core.Card{}
|
||||
for rows.Next() {
|
||||
tem := new(core.Card)
|
||||
err := scanRow(rows, tem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
card = append(card, tem)
|
||||
}
|
||||
return card, nil
|
||||
}
|
|
@ -180,6 +180,14 @@ var migrations = []struct {
|
|||
name: "alter-table-steps-add-column-step-detached",
|
||||
stmt: alterTableStepsAddColumnStepDetached,
|
||||
},
|
||||
{
|
||||
name: "create-table-cards",
|
||||
stmt: createTableCards,
|
||||
},
|
||||
{
|
||||
name: "create-index-cards-card_build",
|
||||
stmt: createIndexCardsCardbuild,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -722,3 +730,22 @@ ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT '';
|
|||
var alterTableStepsAddColumnStepDetached = `
|
||||
ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
`
|
||||
|
||||
//
|
||||
// 017_create_table_cards.sql
|
||||
//
|
||||
|
||||
var createTableCards = `
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id INTEGER PRIMARY KEY AUTO_INCREMENT
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexCardsCardbuild = `
|
||||
CREATE INDEX ix_cards_build ON cards (card_build);
|
||||
`
|
||||
|
|
16
store/shared/migrate/mysql/files/017_create_table_cards.sql
Normal file
16
store/shared/migrate/mysql/files/017_create_table_cards.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-cards
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id INTEGER PRIMARY KEY AUTO_INCREMENT
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
|
||||
-- name: create-index-cards-card-build
|
||||
CREATE INDEX ix_cards_build ON cards (card_build);
|
||||
|
||||
-- name: create-index-cards-card_step
|
||||
CREATE INDEX ix_cards_step ON cards (card_step);
|
|
@ -172,6 +172,14 @@ var migrations = []struct {
|
|||
name: "alter-table-steps-add-column-step-detached",
|
||||
stmt: alterTableStepsAddColumnStepDetached,
|
||||
},
|
||||
{
|
||||
name: "create-table-cards",
|
||||
stmt: createTableCards,
|
||||
},
|
||||
{
|
||||
name: "create-index-cards-card_build",
|
||||
stmt: createIndexCardsCardbuild,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -694,3 +702,22 @@ ALTER TABLE steps ADD COLUMN step_image VARCHAR(1000) NOT NULL DEFAULT '';
|
|||
var alterTableStepsAddColumnStepDetached = `
|
||||
ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
`
|
||||
|
||||
//
|
||||
// 018_create_table_cards.sql
|
||||
//
|
||||
|
||||
var createTableCards = `
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id SERIAL PRIMARY KEY
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexCardsCardbuild = `
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build);
|
||||
`
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-cards
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id SERIAL PRIMARY KEY
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
|
||||
-- name: create-index-cards-card_build
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build);
|
||||
|
||||
-- name: create-index-cards-card_step
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step);
|
|
@ -172,6 +172,14 @@ var migrations = []struct {
|
|||
name: "alter-table-steps-add-column-step-detached",
|
||||
stmt: alterTableStepsAddColumnStepDetached,
|
||||
},
|
||||
{
|
||||
name: "create-table-cards",
|
||||
stmt: createTableCards,
|
||||
},
|
||||
{
|
||||
name: "create-index-cards-card_build",
|
||||
stmt: createIndexCardsCardbuild,
|
||||
},
|
||||
}
|
||||
|
||||
// Migrate performs the database migration. If the migration fails
|
||||
|
@ -696,3 +704,22 @@ ALTER TABLE steps ADD COLUMN step_image TEXT NOT NULL DEFAULT '';
|
|||
var alterTableStepsAddColumnStepDetached = `
|
||||
ALTER TABLE steps ADD COLUMN step_detached BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
`
|
||||
|
||||
//
|
||||
// 017_create_table_cards.sql
|
||||
//
|
||||
|
||||
var createTableCards = `
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
`
|
||||
|
||||
var createIndexCardsCardbuild = `
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build);
|
||||
`
|
||||
|
|
16
store/shared/migrate/sqlite/files/017_create_table_cards.sql
Normal file
16
store/shared/migrate/sqlite/files/017_create_table_cards.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
-- name: create-table-cards
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
card_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,card_build INTEGER
|
||||
,card_stage INTEGER
|
||||
,card_step INTEGER
|
||||
,card_schema TEXT
|
||||
,card_data TEXT
|
||||
);
|
||||
|
||||
-- name: create-index-cards-card_build
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_build ON cards (card_build);
|
||||
|
||||
-- name: create-index-cards-card_step
|
||||
CREATE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step);
|
Loading…
Reference in a new issue