Merge pull request #3149 from drone/feature/dron-101-cards
Feature/dron 101 cards
This commit is contained in:
commit
3c39e541bc
28 changed files with 1870 additions and 4 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)
|
||||
|
|
64
core/card.go
Normal file
64
core/card.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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")
|
||||
)
|
||||
|
||||
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 CardData struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Data []byte `json:"card_data"`
|
||||
}
|
||||
|
||||
// CardStore manages repository cards.
|
||||
type CardStore interface {
|
||||
FindByBuild(ctx context.Context, build int64) ([]*Card, error)
|
||||
Find(ctx context.Context, step int64) (*Card, error)
|
||||
FindData(ctx context.Context, id int64) (io.ReadCloser, error)
|
||||
Create(ctx context.Context, card *Card, data io.ReadCloser) error
|
||||
Delete(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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -285,6 +289,18 @@ 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.CheckAdminAccess(),
|
||||
).Post("/{build}/{stage}/{step}", card.HandleCreate(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
r.With(
|
||||
acl.CheckAdminAccess(),
|
||||
).Delete("/{build}/{stage}/{step}", card.HandleDelete(s.Builds, s.Card, s.Stages, s.Steps, s.Repos))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
113
handler/api/card/create.go
Normal file
113
handler/api/card/create.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
// 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"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"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.Card{
|
||||
Build: build.ID,
|
||||
Stage: stage.ID,
|
||||
Step: step.ID,
|
||||
Schema: in.Schema,
|
||||
}
|
||||
|
||||
err = c.Validate()
|
||||
if err != nil {
|
||||
render.BadRequest(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
data := ioutil.NopCloser(
|
||||
bytes.NewBuffer([]byte(in.Data)),
|
||||
)
|
||||
|
||||
err = cardStore.Create(r.Context(), c, data)
|
||||
if err != nil {
|
||||
render.InternalError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, c, 200)
|
||||
}
|
||||
}
|
174
handler/api/card/create_test.go
Normal file
174
handler/api/card/create_test.go
Normal file
|
@ -0,0 +1,174 @@
|
|||
// 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.Card{
|
||||
Schema: "https://myschema.com",
|
||||
}
|
||||
dummyCard = &core.Card{
|
||||
Id: 1,
|
||||
Build: 1,
|
||||
Stage: 1,
|
||||
Step: 1,
|
||||
Schema: "https://myschema.com",
|
||||
}
|
||||
dummyCardList = []*core.Card{
|
||||
dummyCard,
|
||||
}
|
||||
dummyCardData = ioutil.NopCloser(
|
||||
bytes.NewBuffer([]byte("{\"type\": \"AdaptiveCard\"}")),
|
||||
)
|
||||
)
|
||||
|
||||
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().Create(gomock.Any(), 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_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().Create(gomock.Any(), 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.Find(r.Context(), step.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
err = cardStore.Delete(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().Find(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().Delete(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().Find(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().Find(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().Delete(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.Find(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.FindByBuild(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().FindByBuild(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().FindByBuild(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.Find(r.Context(), step.ID)
|
||||
if err != nil {
|
||||
render.NotFound(w, err)
|
||||
return
|
||||
}
|
||||
cardData, err := cardStore.FindData(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().Find(gomock.Any(), dummyStep.ID).Return(dummyCard, nil)
|
||||
card.EXPECT().FindData(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().Find(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().Find(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
|
||||
|
@ -2894,3 +2894,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
|
||||
}
|
||||
|
||||
// Create mocks base method.
|
||||
func (m *MockCardStore) Create(arg0 context.Context, arg1 *core.Card, arg2 io.ReadCloser) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Create indicates an expected call of Create.
|
||||
func (mr *MockCardStoreMockRecorder) Create(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCardStore)(nil).Create), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Delete mocks base method.
|
||||
func (m *MockCardStore) Delete(arg0 context.Context, arg1 int64) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Delete", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Delete indicates an expected call of Delete.
|
||||
func (mr *MockCardStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCardStore)(nil).Delete), arg0, arg1)
|
||||
}
|
||||
|
||||
// Find mocks base method.
|
||||
func (m *MockCardStore) Find(arg0 context.Context, arg1 int64) (*core.Card, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Find", arg0, arg1)
|
||||
ret0, _ := ret[0].(*core.Card)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Find indicates an expected call of Find.
|
||||
func (mr *MockCardStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCardStore)(nil).Find), arg0, arg1)
|
||||
}
|
||||
|
||||
// FindByBuild mocks base method.
|
||||
func (m *MockCardStore) FindByBuild(arg0 context.Context, arg1 int64) ([]*core.Card, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindByBuild", arg0, arg1)
|
||||
ret0, _ := ret[0].([]*core.Card)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindByBuild indicates an expected call of FindByBuild.
|
||||
func (mr *MockCardStoreMockRecorder) FindByBuild(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByBuild", reflect.TypeOf((*MockCardStore)(nil).FindByBuild), arg0, arg1)
|
||||
}
|
||||
|
||||
// FindData mocks base method.
|
||||
func (m *MockCardStore) FindData(arg0 context.Context, arg1 int64) (io.ReadCloser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindData", arg0, arg1)
|
||||
ret0, _ := ret[0].(io.ReadCloser)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FindData indicates an expected call of FindData.
|
||||
func (mr *MockCardStoreMockRecorder) FindData(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindData", reflect.TypeOf((*MockCardStore)(nil).FindData), arg0, arg1)
|
||||
}
|
||||
|
|
201
store/card/card.go
Normal file
201
store/card/card.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
// 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) FindByBuild(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) Find(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) FindData(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) Create(ctx context.Context, card *core.Card, data io.ReadCloser) error {
|
||||
if c.db.Driver() == db.Postgres {
|
||||
return c.createPostgres(ctx, card, data)
|
||||
}
|
||||
return c.create(ctx, card, data)
|
||||
}
|
||||
|
||||
func (c *cardStore) create(ctx context.Context, card *core.Card, data io.ReadCloser) error {
|
||||
return c.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
cardData, err := ioutil.ReadAll(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := toSaveCardParams(card, cardData)
|
||||
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.Card, data io.ReadCloser) error {
|
||||
return c.db.Lock(func(execer db.Execer, binder db.Binder) error {
|
||||
cardData, err := ioutil.ReadAll(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := toSaveCardParams(card, cardData)
|
||||
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) Delete(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) FindByBuild(ctx context.Context, build int64) ([]*core.Card, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) Find(ctx context.Context, step int64) (*core.Card, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) FindData(ctx context.Context, id int64) (io.ReadCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (noop) Create(ctx context.Context, card *core.Card, data io.ReadCloser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (noop) Delete(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
133
store/card/card_test.go
Normal file
133
store/card/card_test.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
package card
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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.Card{
|
||||
Id: 1,
|
||||
Build: 1,
|
||||
Stage: 2,
|
||||
Step: 3,
|
||||
Schema: "https://myschema.com",
|
||||
}
|
||||
buf := ioutil.NopCloser(
|
||||
bytes.NewBuffer([]byte("{\"type\": \"AdaptiveCard\"}")),
|
||||
)
|
||||
err := store.Create(noContext, item, buf)
|
||||
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("Find", testFindCard(store))
|
||||
t.Run("FindData", testFindCardData(store))
|
||||
t.Run("Delete", testCardDelete(store))
|
||||
}
|
||||
}
|
||||
|
||||
func testFindCardByBuild(card *cardStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
item, err := card.FindByBuild(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.Find(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.FindData(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.Find(noContext, 3)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
err = store.Delete(noContext, card.Id)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
_, err = store.Find(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.Card, data []byte) (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": 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 UNIQUE 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 UNIQUE 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 UNIQUE INDEX IF NOT EXISTS ix_cards_step ON cards (card_step);
|
Loading…
Reference in a new issue