Skip to content

Commit

Permalink
Merge pull request #209 from kilosonc/feat/badge
Browse files Browse the repository at this point in the history
feat: support badge
  • Loading branch information
kilosonc authored Jan 10, 2024
2 parents 4c1159d + 868754f commit e417dbd
Show file tree
Hide file tree
Showing 20 changed files with 1,781 additions and 4 deletions.
5 changes: 5 additions & 0 deletions core/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
accesstokenctl "github.com/horizoncd/horizon/core/controller/accesstoken"
applicationctl "github.com/horizoncd/horizon/core/controller/application"
applicationregionctl "github.com/horizoncd/horizon/core/controller/applicationregion"
badgectl "github.com/horizoncd/horizon/core/controller/badge"
"github.com/horizoncd/horizon/core/controller/build"
clusterctl "github.com/horizoncd/horizon/core/controller/cluster"
codectl "github.com/horizoncd/horizon/core/controller/code"
Expand Down Expand Up @@ -84,6 +85,7 @@ import (
accessv2 "github.com/horizoncd/horizon/core/http/api/v2/access"
accesstokenv2 "github.com/horizoncd/horizon/core/http/api/v2/accesstoken"
applicationregionv2 "github.com/horizoncd/horizon/core/http/api/v2/applicationregion"
"github.com/horizoncd/horizon/core/http/api/v2/badge"
clusterv2 "github.com/horizoncd/horizon/core/http/api/v2/cluster"
codev2 "github.com/horizoncd/horizon/core/http/api/v2/code"
environmentv2 "github.com/horizoncd/horizon/core/http/api/v2/environment"
Expand Down Expand Up @@ -530,6 +532,7 @@ func Init(ctx context.Context, flags *Flags, coreConfig *config.Config) {
scopeCtl = scopectl.NewController(parameter)
webhookCtl = webhookctl.NewController(parameter)
eventCtl = eventctl.NewController(parameter)
badgeCtl = badgectl.NewController(parameter)
)

var (
Expand Down Expand Up @@ -589,6 +592,7 @@ func Init(ctx context.Context, flags *Flags, coreConfig *config.Config) {
terminalAPIV2 = terminalv2.NewAPI(terminalCtl)
userAPIV2 = userv2.NewAPI(userCtl, store)
webhookAPIV2 = webhookv2.NewAPI(webhookCtl)
badgeAPIV2 = badge.NewAPI(badgeCtl)
)

// start jobs
Expand Down Expand Up @@ -701,6 +705,7 @@ func Init(ctx context.Context, flags *Flags, coreConfig *config.Config) {
terminalAPIV2,
userAPIV2,
webhookAPIV2,
badgeAPIV2,
}

// start cloud event server
Expand Down
178 changes: 178 additions & 0 deletions core/controller/badge/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package badge

import (
"context"
"net/url"

"github.com/horizoncd/horizon/core/common"
herrors "github.com/horizoncd/horizon/core/errors"
appmanager "github.com/horizoncd/horizon/pkg/application/manager"
"github.com/horizoncd/horizon/pkg/badge/manager"
"github.com/horizoncd/horizon/pkg/badge/models"
clustermanager "github.com/horizoncd/horizon/pkg/cluster/manager"
perror "github.com/horizoncd/horizon/pkg/errors"
groupmanager "github.com/horizoncd/horizon/pkg/group/manager"
"github.com/horizoncd/horizon/pkg/param"
)

type Controller interface {
CreateBadge(ctx context.Context, resourceType string, resourceID uint, badge *Create) (*Badge, error)
UpdateBadge(ctx context.Context, id uint, badge *Update) (*Badge, error)
UpdateBadgeByName(ctx context.Context, resourceType string,
resourceID uint, name string, badge *Update) (*Badge, error)
ListBadges(ctx context.Context, resourceType string, resourceID uint) ([]*Badge, error)
GetBadge(ctx context.Context, id uint) (*Badge, error)
GetBadgeByName(ctx context.Context, resourceType string, resourceID uint, name string) (*Badge, error)
DeleteBadge(ctx context.Context, id uint) error
DeleteBadgeByName(ctx context.Context, resourceType string, resourceID uint, name string) error
}

type controller struct {
badgeMgr manager.Manager
clusterMgr clustermanager.Manager
applicationMgr appmanager.Manager
groupMgr groupmanager.Manager
}

func NewController(param *param.Param) Controller {
return &controller{
badgeMgr: param.BadgeMgr,
clusterMgr: param.ClusterMgr,
applicationMgr: param.ApplicationMgr,
groupMgr: param.GroupMgr,
}
}

func (c *controller) checkResource(ctx context.Context, resourceType string, resourceID uint) error {
switch resourceType {
case common.ResourceApplication:
if _, err := c.applicationMgr.GetByID(ctx, resourceID); err != nil {
return err
}
case common.ResourceCluster:
if _, err := c.clusterMgr.GetByID(ctx, resourceID); err != nil {
return err
}
case common.ResourceGroup:
if _, err := c.groupMgr.GetByID(ctx, resourceID); err != nil {
return err
}
default:
return perror.Wrapf(herrors.ErrParamInvalid, "invalid resource type: %s", resourceType)
}
return nil
}

func (c *controller) CreateBadge(ctx context.Context, resourceType string,
resourceID uint, badge *Create) (*Badge, error) {
if err := c.checkResource(ctx, resourceType, resourceID); err != nil {
return nil, err
}

svgURL, err := url.Parse(badge.SvgLink)
if err != nil || (svgURL.Scheme != "https" && svgURL.Scheme != "http") {
return nil, perror.Wrapf(herrors.ErrParamInvalid, "invalid svg link: %s", badge.SvgLink)
}
if badge.RedirectLink != "" {
redirectURL, err := url.Parse(badge.RedirectLink)
if err != nil || (redirectURL.Scheme != "https" && redirectURL.Scheme != "http") {
return nil, perror.Wrapf(herrors.ErrParamInvalid, "invalid redirect link: %s", badge.RedirectLink)
}
}
daoBadge := &models.Badge{
ResourceType: resourceType,
ResourceID: resourceID,
Name: badge.Name,
SvgLink: badge.SvgLink,
RedirectLink: badge.RedirectLink,
}
daoBadge, err = c.badgeMgr.Create(ctx, daoBadge)
if err != nil {
return nil, err
}
result := &Badge{}
result.FromDAO(daoBadge)
return result, nil
}

func (c *controller) UpdateBadge(ctx context.Context, id uint, badge *Update) (*Badge, error) {
daoBadge := &models.Badge{
ID: id,
}

if badge.SvgLink != nil {
daoBadge.SvgLink = *badge.SvgLink
}
if badge.RedirectLink != nil {
daoBadge.RedirectLink = *badge.RedirectLink
}
var err error
daoBadge, err = c.badgeMgr.Update(ctx, daoBadge)
if err != nil {
return nil, err
}
result := &Badge{}
result.FromDAO(daoBadge)
return result, nil
}

func (c *controller) UpdateBadgeByName(ctx context.Context, resourceType string,
resourceID uint, name string, badge *Update) (*Badge, error) {
daoBadge := &models.Badge{}
if badge.SvgLink != nil {
daoBadge.SvgLink = *badge.SvgLink
}
if badge.RedirectLink != nil {
daoBadge.RedirectLink = *badge.RedirectLink
}
var err error
daoBadge, err = c.badgeMgr.UpdateByName(ctx, resourceType, resourceID, name, daoBadge)
if err != nil {
return nil, err
}
result := &Badge{}
result.FromDAO(daoBadge)
return result, nil
}

func (c *controller) ListBadges(ctx context.Context, resourceType string, resourceID uint) ([]*Badge, error) {
daoBadges, err := c.badgeMgr.List(ctx, resourceType, resourceID)
if err != nil {
return nil, err
}
result := make([]*Badge, len(daoBadges))
for i, daoBadge := range daoBadges {
result[i] = &Badge{}
result[i].FromDAO(daoBadge)
}
return result, nil
}

func (c *controller) GetBadge(ctx context.Context, id uint) (*Badge, error) {
daoBadge, err := c.badgeMgr.Get(ctx, id)
if err != nil {
return nil, err
}
result := &Badge{}
result.FromDAO(daoBadge)
return result, nil
}

func (c *controller) GetBadgeByName(ctx context.Context, resourceType string,
resourceID uint, name string) (*Badge, error) {
daoBadge, err := c.badgeMgr.GetByName(ctx, resourceType, resourceID, name)
if err != nil {
return nil, err
}
result := &Badge{}
result.FromDAO(daoBadge)
return result, nil
}

func (c *controller) DeleteBadge(ctx context.Context, id uint) error {
return c.badgeMgr.Delete(ctx, id)
}

func (c *controller) DeleteBadgeByName(ctx context.Context, resourceType string, resourceID uint, name string) error {
return c.badgeMgr.DeleteByName(ctx, resourceType, resourceID, name)
}
147 changes: 147 additions & 0 deletions core/controller/badge/controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright © 2023 Horizoncd.
//
// 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 badge

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"gorm.io/gorm"

"github.com/horizoncd/horizon/core/common"
"github.com/horizoncd/horizon/lib/orm"
userauth "github.com/horizoncd/horizon/pkg/authentication/user"
badgemodel "github.com/horizoncd/horizon/pkg/badge/models"
clustermodel "github.com/horizoncd/horizon/pkg/cluster/models"
membermodels "github.com/horizoncd/horizon/pkg/member/models"
"github.com/horizoncd/horizon/pkg/param"
"github.com/horizoncd/horizon/pkg/param/managerparam"
"github.com/horizoncd/horizon/pkg/server/global"
usermodels "github.com/horizoncd/horizon/pkg/user/models"
)

var (
ctx = context.Background()
db *gorm.DB
mgr *managerparam.Manager
)

func createContext() {
db, _ = orm.NewSqliteDB("")
if err := db.AutoMigrate(&usermodels.User{},
&clustermodel.Cluster{},
&membermodels.Member{},
&badgemodel.Badge{}); err != nil {
panic(err)
}
mgr = managerparam.InitManager(db)
ctx = context.Background()
// nolint
ctx = common.WithContext(ctx, &userauth.DefaultInfo{
Name: "Jerry",
ID: 1,
Admin: true,
})
}

// nolint
func Test(t *testing.T) {
createContext()
ctrl := NewController(&param.Param{Manager: mgr})

redirectLink := "https://https://horizoncd.github.io"
_, err := ctrl.CreateBadge(ctx, common.ResourceCluster, 0, &Create{
Name: "horizon",
SvgLink: "https://horizoncd.io",
RedirectLink: redirectLink,
})

c, err := mgr.ClusterMgr.Create(ctx,
&clustermodel.Cluster{
Model: global.Model{
ID: 1,
},
Name: "test",
}, nil, nil)

assert.Nil(t, err)

badge, err := ctrl.CreateBadge(ctx, common.ResourceCluster, c.ID, &Create{
Name: "horizon",
SvgLink: "https://github.com/horizoncd/horizon/svgs/horizon.svg",
RedirectLink: redirectLink,
})
assert.Nil(t, err)

badgeGot, err := ctrl.GetBadge(ctx, badge.ID)
assert.Nil(t, err)
assert.Equal(t, badge.ID, badgeGot.ID)
assert.Equal(t, badge.Name, badgeGot.Name)

updatedSvgLink := "https://github.com/horizoncd/horizon/svgs/horizon2.svg"
badgeUpdated, err := ctrl.UpdateBadge(ctx, badge.ID, &Update{
SvgLink: &updatedSvgLink,
})

assert.Equal(t, badgeUpdated.SvgLink, "https://github.com/horizoncd/horizon/svgs/horizon2.svg")
assert.Equal(t, badgeUpdated.RedirectLink, badgeGot.RedirectLink)

badgeGot, err = ctrl.GetBadgeByName(ctx, common.ResourceCluster, c.ID, badge.Name)
assert.Nil(t, err)
assert.Equal(t, badge.ID, badgeGot.ID)
assert.Equal(t, badgeUpdated.SvgLink, "https://github.com/horizoncd/horizon/svgs/horizon2.svg")
assert.Equal(t, badgeUpdated.RedirectLink, badgeGot.RedirectLink)

_, err = ctrl.GetBadgeByName(ctx, common.ResourceCluster, c.ID, "not-exist")
assert.NotNil(t, err)

updatedRedirectLink := "https://horizoncd.github.io2"
badge2, err := ctrl.CreateBadge(ctx, common.ResourceCluster, c.ID, &Create{
Name: "horizon2",
SvgLink: "https://github.com/horizoncd/horizon/svgs/horizon.svg2",
RedirectLink: updatedRedirectLink,
})
assert.Nil(t, err)

badges, err := ctrl.ListBadges(ctx, common.ResourceCluster, c.ID)
assert.Nil(t, err)
assert.Equal(t, 2, len(badges))
for _, b := range badges {
if b.ID == badge.ID {
assert.Equal(t, b.Name, badgeUpdated.Name)
assert.Equal(t, b.SvgLink, badgeUpdated.SvgLink)
assert.Equal(t, b.RedirectLink, badgeUpdated.RedirectLink)
} else {
assert.Equal(t, b.Name, badge2.Name)
assert.Equal(t, b.SvgLink, badge2.SvgLink)
assert.Equal(t, b.RedirectLink, badge2.RedirectLink)
}
}

err = ctrl.DeleteBadge(ctx, badge.ID)
assert.Nil(t, err)

badges, err = ctrl.ListBadges(ctx, common.ResourceCluster, c.ID)
assert.Nil(t, err)
assert.Equal(t, 1, len(badges))

err = ctrl.DeleteBadgeByName(ctx, common.ResourceCluster, c.ID, badge2.Name)
assert.Nil(t, err)

badges, err = ctrl.ListBadges(ctx, common.ResourceCluster, c.ID)
assert.Nil(t, err)
assert.Equal(t, 0, len(badges))
}
Loading

0 comments on commit e417dbd

Please sign in to comment.