Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Add a new license type for OSS #186

Merged
merged 5 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/interactor/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type (
UpdatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error)
DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error)

CountDeployments(ctx context.Context) (int, error)
SearchDeployments(ctx context.Context, u *ent.User, s []deployment.Status, owned bool, from time.Time, to time.Time, page, perPage int) ([]*ent.Deployment, error)
ListInactiveDeploymentsLessThanTime(ctx context.Context, t time.Time, page, perPage int) ([]*ent.Deployment, error)
ListDeploymentsOfRepo(ctx context.Context, r *ent.Repo, env string, status string, page, perPage int) ([]*ent.Deployment, error)
Expand Down
26 changes: 20 additions & 6 deletions internal/interactor/license.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Copyright 2021 Gitploy.IO Inc. All rights reserved.
// Use of this source code is governed by the Gitploy Non-Commercial License
// that can be found in the LICENSE file.

// +build !oss

package interactor

import (
Expand All @@ -10,20 +16,28 @@ import (

func (i *Interactor) GetLicense(ctx context.Context) (*vo.License, error) {
var (
cnt int
d *vo.SigningData
err error
memberCnt int
deploymentCnt int
d *vo.SigningData
err error
)

if cnt, err = i.Store.CountUsers(ctx); err != nil {
if memberCnt, err = i.Store.CountUsers(ctx); err != nil {
return nil, e.NewError(
e.ErrorCodeInternalError,
err,
)
}

if deploymentCnt, err = i.Store.CountDeployments(ctx); err != nil {
return nil, e.NewError(
e.ErrorCodeInternalError,
err,
)
}

if i.licenseKey == "" {
lic := vo.NewTrialLicense(cnt)
lic := vo.NewTrialLicense(memberCnt, deploymentCnt)
return lic, nil
}

Expand All @@ -34,6 +48,6 @@ func (i *Interactor) GetLicense(ctx context.Context) (*vo.License, error) {
)
}

lic := vo.NewStandardLicense(cnt, d)
lic := vo.NewStandardLicense(memberCnt, d)
return lic, nil
}
13 changes: 13 additions & 0 deletions internal/interactor/license_oss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// +build oss

package interactor

import (
"context"

"github.com/gitploy-io/gitploy/vo"
)

func (i *Interactor) GetLicense(ctx context.Context) (*vo.License, error) {
return vo.NewOSSLicense(), nil
}
39 changes: 39 additions & 0 deletions internal/interactor/license_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package interactor

import (
"context"
"testing"

"github.com/gitploy-io/gitploy/internal/interactor/mock"
"github.com/gitploy-io/gitploy/vo"
"github.com/golang/mock/gomock"
)

func TestStore_GetLicense(t *testing.T) {
t.Run("Return the trial license when the signing data is nil.", func(t *testing.T) {
ctrl := gomock.NewController(t)
store := mock.NewMockStore(ctrl)

t.Log("MOCK - return the count of users.")
store.
EXPECT().
CountUsers(gomock.AssignableToTypeOf(context.Background())).
Return(vo.TrialMemberLimit, nil)

store.
EXPECT().
CountDeployments(gomock.AssignableToTypeOf(context.Background())).
Return(vo.TrialDeploymentLimit, nil)

i := &Interactor{Store: store}

lic, err := i.GetLicense(context.Background())
if err != nil {
t.Fatalf("GetLicense returns an error: %s", err)
}

if !lic.IsTrial() {
t.Fatalf("GetLicense = %v, wanted %v", lic.Kind, vo.LicenseKindTrial)
}
})
}
15 changes: 15 additions & 0 deletions internal/interactor/mock/pkg.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions internal/pkg/store/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import (
"github.com/gitploy-io/gitploy/ent/predicate"
)

func (s *Store) CountDeployments(ctx context.Context) (int, error) {
return s.c.Deployment.
Query().
Count(ctx)
}

func (s *Store) SearchDeployments(ctx context.Context, u *ent.User, ss []deployment.Status, owned bool, from time.Time, to time.Time, page, perPage int) ([]*ent.Deployment, error) {
statusIn := func(ss []deployment.Status) predicate.Deployment {
if len(ss) == 0 {
Expand Down
6 changes: 5 additions & 1 deletion internal/server/api/shared/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ func (m *Middleware) IsLicenseExpired() gin.HandlerFunc {
return
}

if lic.IsOSS() {
return
}

Comment on lines +37 to +40
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip when the license is oss

if lic.IsOverLimit() {
gb.AbortWithErrorResponse(c, http.StatusPaymentRequired, "The member count is over the limit.")
return
}

if !lic.IsTrial() && lic.IsExpired() {
if lic.IsStandard() && lic.IsExpired() {
now := time.Now()
if lic.ExpiredAt.Add(extraDuration).Before(now) {
gb.AbortWithErrorResponse(c, http.StatusPaymentRequired, "The license is expired.")
Expand Down
31 changes: 29 additions & 2 deletions internal/server/api/shared/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,41 @@ import (
func TestMiddleware_IsLicenseExpired(t *testing.T) {
month := 30 * 24 * time.Hour

t.Run("Return 200 when the license is OSS.", func(t *testing.T) {
ctrl := gomock.NewController(t)
m := mock.NewMockInteractor(ctrl)

m.
EXPECT().
GetLicense(gomock.Any()).
Return(vo.NewOSSLicense(), nil)

gin.SetMode(gin.ReleaseMode)
router := gin.New()

lm := NewMiddleware(m)
router.GET("/repos", lm.IsLicenseExpired(), func(c *gin.Context) {
c.Status(http.StatusOK)
})

req, _ := http.NewRequest("GET", "/repos", nil)
w := httptest.NewRecorder()

router.ServeHTTP(w, req)

if w.Code != http.StatusOK {
t.Fatalf("IsLicenseExpired = %v, wanted %v", w.Code, http.StatusOK)
}
})

t.Run("Return 402 error when the count of member is over the limit.", func(t *testing.T) {
ctrl := gomock.NewController(t)
m := mock.NewMockInteractor(ctrl)

m.
EXPECT().
GetLicense(gomock.Any()).
Return(vo.NewTrialLicense(7), nil)
Return(vo.NewTrialLicense(vo.TrialMemberLimit+1, vo.TrialDeploymentLimit), nil)

gin.SetMode(gin.ReleaseMode)
router := gin.New()
Expand All @@ -49,7 +76,7 @@ func TestMiddleware_IsLicenseExpired(t *testing.T) {
m.
EXPECT().
GetLicense(gomock.Any()).
Return(vo.NewTrialLicense(vo.TrialMemberLimit), nil)
Return(vo.NewTrialLicense(vo.TrialMemberLimit, vo.TrialDeploymentLimit), nil)

gin.SetMode(gin.ReleaseMode)
router := gin.New()
Expand Down
60 changes: 44 additions & 16 deletions vo/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@ package vo
import "time"

const (
TrialMemberLimit = 5
TrialMemberLimit = 5
TrialDeploymentLimit = 5000
)

const (
LicenseKindTrial LicenseKind = "trial"
// LicenseKindOSS is a license for the community edition.
LicenseKindOSS LicenseKind = "oss"
// LicenseKindTrial is a trial license of the enterprise edition.
LicenseKindTrial LicenseKind = "trial"
// LicenseKindStandard is a license of the enterprise edition.
LicenseKindStandard LicenseKind = "standard"
)

type (
LicenseKind string

License struct {
Kind LicenseKind `json:"kind"`
MemberCount int `json:"member_count"`
MemberLimit int `json:"memeber_limit"`
ExpiredAt time.Time `json:"expired_at"`
Kind LicenseKind `json:"kind"`
MemberCount int `json:"member_count"`
MemberLimit int `json:"memeber_limit"`
DeploymentCount int `json:"deployment_count"`
DeploymentLimit int `json:"deployment_limit"`
ExpiredAt time.Time `json:"expired_at"`
}

// SigningData marshal and unmarshal the content of license.
Expand All @@ -28,31 +35,52 @@ type (
}
)

func NewTrialLicense(cnt int) *License {
func NewOSSLicense() *License {
return &License{
Kind: LicenseKindTrial,
MemberCount: cnt,
MemberLimit: TrialMemberLimit,
Kind: LicenseKindOSS,
MemberCount: -1,
DeploymentCount: -1,
Comment on lines +40 to +42
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsOverLimit of the OSS License always returns false.

}
}

func NewStandardLicense(cnt int, d *SigningData) *License {
func NewTrialLicense(memberCnt, deploymentCnt int) *License {
return &License{
Kind: LicenseKindStandard,
MemberCount: cnt,
MemberLimit: d.MemberLimit,
ExpiredAt: d.ExpiredAt,
Kind: LicenseKindTrial,
MemberCount: memberCnt,
MemberLimit: TrialMemberLimit,
DeploymentCount: deploymentCnt,
DeploymentLimit: TrialDeploymentLimit,
}
}

func NewStandardLicense(memberCnt int, d *SigningData) *License {
return &License{
Kind: LicenseKindStandard,
MemberCount: memberCnt,
MemberLimit: d.MemberLimit,
DeploymentCount: -1,
ExpiredAt: d.ExpiredAt,
}
}

func (l *License) IsOSS() bool {
return l.Kind == LicenseKindOSS
}

func (l *License) IsTrial() bool {
return l.Kind == LicenseKindTrial
}

func (l *License) IsStandard() bool {
return l.Kind == LicenseKindStandard
}

// IsOverLimit verify it is over the limit of the license.
func (l *License) IsOverLimit() bool {
return l.MemberCount > l.MemberLimit
return l.MemberCount > l.MemberLimit || l.DeploymentCount > l.DeploymentLimit
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add deployment limit to verify the license.

}

// IsExpired verify that the license is expired or not.
func (l *License) IsExpired() bool {
return l.ExpiredAt.Before(time.Now())
}
35 changes: 28 additions & 7 deletions vo/license_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,44 @@ import (
)

func TestLicense_IsOverLimit(t *testing.T) {
t.Run("Return false when the count of member is over the limit.", func(t *testing.T) {
l := NewTrialLicense(6)
t.Run("Return false when the license is OSS.", func(t *testing.T) {
l := NewOSSLicense()

expected := false
if finished := l.IsOverLimit(); finished != expected {
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
}
})

t.Run("Return true when the trial license is over the member limit.", func(t *testing.T) {
l := NewTrialLicense(TrialMemberLimit+1, 0)

expected := true
if finished := l.IsOverLimit(); finished != expected {
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
}
})

t.Run("Return true when the count of member is under the limit.", func(t *testing.T) {
tl := NewTrialLicense(5)
t.Run("Return true when the trial license is over the deployment limit.", func(t *testing.T) {
l := NewTrialLicense(5, TrialDeploymentLimit+1)

if finished := tl.IsOverLimit(); finished != false {
t.Fatalf("IsOverLimit = %v, wanted %v", finished, false)
expected := true
if finished := l.IsOverLimit(); finished != expected {
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
}
})

t.Run("Return false when the trial license is less than or equal to the limit.", func(t *testing.T) {
l := NewTrialLicense(TrialMemberLimit, TrialDeploymentLimit)

expected := false
if finished := l.IsOverLimit(); finished != expected {
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
}
})

sl := NewStandardLicense(10, &SigningData{
t.Run("Return true when the standard license is less than the limit.", func(t *testing.T) {
sl := NewStandardLicense(20, &SigningData{
MemberLimit: 20,
ExpiredAt: time.Now(),
})
Expand Down