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

Commit 0f94f87

Browse files
author
Noah Hanjun Lee
authored
Add a new license type for OSS (#186)
* Add OSS license * Add limit count of deployment.
1 parent 403cb42 commit 0f94f87

File tree

10 files changed

+200
-32
lines changed

10 files changed

+200
-32
lines changed

internal/interactor/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type (
4545
UpdatePerm(ctx context.Context, p *ent.Perm) (*ent.Perm, error)
4646
DeletePermsOfUserLessThanSyncedAt(ctx context.Context, u *ent.User, t time.Time) (int, error)
4747

48+
CountDeployments(ctx context.Context) (int, error)
4849
SearchDeployments(ctx context.Context, u *ent.User, s []deployment.Status, owned bool, from time.Time, to time.Time, page, perPage int) ([]*ent.Deployment, error)
4950
ListInactiveDeploymentsLessThanTime(ctx context.Context, t time.Time, page, perPage int) ([]*ent.Deployment, error)
5051
ListDeploymentsOfRepo(ctx context.Context, r *ent.Repo, env string, status string, page, perPage int) ([]*ent.Deployment, error)

internal/interactor/license.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// Copyright 2021 Gitploy.IO Inc. All rights reserved.
2+
// Use of this source code is governed by the Gitploy Non-Commercial License
3+
// that can be found in the LICENSE file.
4+
5+
// +build !oss
6+
17
package interactor
28

39
import (
@@ -10,20 +16,28 @@ import (
1016

1117
func (i *Interactor) GetLicense(ctx context.Context) (*vo.License, error) {
1218
var (
13-
cnt int
14-
d *vo.SigningData
15-
err error
19+
memberCnt int
20+
deploymentCnt int
21+
d *vo.SigningData
22+
err error
1623
)
1724

18-
if cnt, err = i.Store.CountUsers(ctx); err != nil {
25+
if memberCnt, err = i.Store.CountUsers(ctx); err != nil {
26+
return nil, e.NewError(
27+
e.ErrorCodeInternalError,
28+
err,
29+
)
30+
}
31+
32+
if deploymentCnt, err = i.Store.CountDeployments(ctx); err != nil {
1933
return nil, e.NewError(
2034
e.ErrorCodeInternalError,
2135
err,
2236
)
2337
}
2438

2539
if i.licenseKey == "" {
26-
lic := vo.NewTrialLicense(cnt)
40+
lic := vo.NewTrialLicense(memberCnt, deploymentCnt)
2741
return lic, nil
2842
}
2943

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

37-
lic := vo.NewStandardLicense(cnt, d)
51+
lic := vo.NewStandardLicense(memberCnt, d)
3852
return lic, nil
3953
}

internal/interactor/license_oss.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// +build oss
2+
3+
package interactor
4+
5+
import (
6+
"context"
7+
8+
"github.com/gitploy-io/gitploy/vo"
9+
)
10+
11+
func (i *Interactor) GetLicense(ctx context.Context) (*vo.License, error) {
12+
return vo.NewOSSLicense(), nil
13+
}

internal/interactor/license_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package interactor
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gitploy-io/gitploy/internal/interactor/mock"
8+
"github.com/gitploy-io/gitploy/vo"
9+
"github.com/golang/mock/gomock"
10+
)
11+
12+
func TestStore_GetLicense(t *testing.T) {
13+
t.Run("Return the trial license when the signing data is nil.", func(t *testing.T) {
14+
ctrl := gomock.NewController(t)
15+
store := mock.NewMockStore(ctrl)
16+
17+
t.Log("MOCK - return the count of users.")
18+
store.
19+
EXPECT().
20+
CountUsers(gomock.AssignableToTypeOf(context.Background())).
21+
Return(vo.TrialMemberLimit, nil)
22+
23+
store.
24+
EXPECT().
25+
CountDeployments(gomock.AssignableToTypeOf(context.Background())).
26+
Return(vo.TrialDeploymentLimit, nil)
27+
28+
i := &Interactor{Store: store}
29+
30+
lic, err := i.GetLicense(context.Background())
31+
if err != nil {
32+
t.Fatalf("GetLicense returns an error: %s", err)
33+
}
34+
35+
if !lic.IsTrial() {
36+
t.Fatalf("GetLicense = %v, wanted %v", lic.Kind, vo.LicenseKindTrial)
37+
}
38+
})
39+
}

internal/interactor/mock/pkg.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/pkg/store/deployment.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import (
1111
"github.com/gitploy-io/gitploy/ent/predicate"
1212
)
1313

14+
func (s *Store) CountDeployments(ctx context.Context) (int, error) {
15+
return s.c.Deployment.
16+
Query().
17+
Count(ctx)
18+
}
19+
1420
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) {
1521
statusIn := func(ss []deployment.Status) predicate.Deployment {
1622
if len(ss) == 0 {

internal/server/api/shared/middleware.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ func (m *Middleware) IsLicenseExpired() gin.HandlerFunc {
3434
return
3535
}
3636

37+
if lic.IsOSS() {
38+
return
39+
}
40+
3741
if lic.IsOverLimit() {
3842
gb.AbortWithErrorResponse(c, http.StatusPaymentRequired, "The member count is over the limit.")
3943
return
4044
}
4145

42-
if !lic.IsTrial() && lic.IsExpired() {
46+
if lic.IsStandard() && lic.IsExpired() {
4347
now := time.Now()
4448
if lic.ExpiredAt.Add(extraDuration).Before(now) {
4549
gb.AbortWithErrorResponse(c, http.StatusPaymentRequired, "The license is expired.")

internal/server/api/shared/middleware_test.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,41 @@ import (
1515
func TestMiddleware_IsLicenseExpired(t *testing.T) {
1616
month := 30 * 24 * time.Hour
1717

18+
t.Run("Return 200 when the license is OSS.", func(t *testing.T) {
19+
ctrl := gomock.NewController(t)
20+
m := mock.NewMockInteractor(ctrl)
21+
22+
m.
23+
EXPECT().
24+
GetLicense(gomock.Any()).
25+
Return(vo.NewOSSLicense(), nil)
26+
27+
gin.SetMode(gin.ReleaseMode)
28+
router := gin.New()
29+
30+
lm := NewMiddleware(m)
31+
router.GET("/repos", lm.IsLicenseExpired(), func(c *gin.Context) {
32+
c.Status(http.StatusOK)
33+
})
34+
35+
req, _ := http.NewRequest("GET", "/repos", nil)
36+
w := httptest.NewRecorder()
37+
38+
router.ServeHTTP(w, req)
39+
40+
if w.Code != http.StatusOK {
41+
t.Fatalf("IsLicenseExpired = %v, wanted %v", w.Code, http.StatusOK)
42+
}
43+
})
44+
1845
t.Run("Return 402 error when the count of member is over the limit.", func(t *testing.T) {
1946
ctrl := gomock.NewController(t)
2047
m := mock.NewMockInteractor(ctrl)
2148

2249
m.
2350
EXPECT().
2451
GetLicense(gomock.Any()).
25-
Return(vo.NewTrialLicense(7), nil)
52+
Return(vo.NewTrialLicense(vo.TrialMemberLimit+1, vo.TrialDeploymentLimit), nil)
2653

2754
gin.SetMode(gin.ReleaseMode)
2855
router := gin.New()
@@ -49,7 +76,7 @@ func TestMiddleware_IsLicenseExpired(t *testing.T) {
4976
m.
5077
EXPECT().
5178
GetLicense(gomock.Any()).
52-
Return(vo.NewTrialLicense(vo.TrialMemberLimit), nil)
79+
Return(vo.NewTrialLicense(vo.TrialMemberLimit, vo.TrialDeploymentLimit), nil)
5380

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

vo/license.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,29 @@ package vo
33
import "time"
44

55
const (
6-
TrialMemberLimit = 5
6+
TrialMemberLimit = 5
7+
TrialDeploymentLimit = 5000
78
)
89

910
const (
10-
LicenseKindTrial LicenseKind = "trial"
11+
// LicenseKindOSS is a license for the community edition.
12+
LicenseKindOSS LicenseKind = "oss"
13+
// LicenseKindTrial is a trial license of the enterprise edition.
14+
LicenseKindTrial LicenseKind = "trial"
15+
// LicenseKindStandard is a license of the enterprise edition.
1116
LicenseKindStandard LicenseKind = "standard"
1217
)
1318

1419
type (
1520
LicenseKind string
1621

1722
License struct {
18-
Kind LicenseKind `json:"kind"`
19-
MemberCount int `json:"member_count"`
20-
MemberLimit int `json:"memeber_limit"`
21-
ExpiredAt time.Time `json:"expired_at"`
23+
Kind LicenseKind `json:"kind"`
24+
MemberCount int `json:"member_count"`
25+
MemberLimit int `json:"memeber_limit"`
26+
DeploymentCount int `json:"deployment_count"`
27+
DeploymentLimit int `json:"deployment_limit"`
28+
ExpiredAt time.Time `json:"expired_at"`
2229
}
2330

2431
// SigningData marshal and unmarshal the content of license.
@@ -28,31 +35,52 @@ type (
2835
}
2936
)
3037

31-
func NewTrialLicense(cnt int) *License {
38+
func NewOSSLicense() *License {
3239
return &License{
33-
Kind: LicenseKindTrial,
34-
MemberCount: cnt,
35-
MemberLimit: TrialMemberLimit,
40+
Kind: LicenseKindOSS,
41+
MemberCount: -1,
42+
DeploymentCount: -1,
3643
}
3744
}
3845

39-
func NewStandardLicense(cnt int, d *SigningData) *License {
46+
func NewTrialLicense(memberCnt, deploymentCnt int) *License {
4047
return &License{
41-
Kind: LicenseKindStandard,
42-
MemberCount: cnt,
43-
MemberLimit: d.MemberLimit,
44-
ExpiredAt: d.ExpiredAt,
48+
Kind: LicenseKindTrial,
49+
MemberCount: memberCnt,
50+
MemberLimit: TrialMemberLimit,
51+
DeploymentCount: deploymentCnt,
52+
DeploymentLimit: TrialDeploymentLimit,
4553
}
4654
}
4755

56+
func NewStandardLicense(memberCnt int, d *SigningData) *License {
57+
return &License{
58+
Kind: LicenseKindStandard,
59+
MemberCount: memberCnt,
60+
MemberLimit: d.MemberLimit,
61+
DeploymentCount: -1,
62+
ExpiredAt: d.ExpiredAt,
63+
}
64+
}
65+
66+
func (l *License) IsOSS() bool {
67+
return l.Kind == LicenseKindOSS
68+
}
69+
4870
func (l *License) IsTrial() bool {
4971
return l.Kind == LicenseKindTrial
5072
}
5173

74+
func (l *License) IsStandard() bool {
75+
return l.Kind == LicenseKindStandard
76+
}
77+
78+
// IsOverLimit verify it is over the limit of the license.
5279
func (l *License) IsOverLimit() bool {
53-
return l.MemberCount > l.MemberLimit
80+
return l.MemberCount > l.MemberLimit || l.DeploymentCount > l.DeploymentLimit
5481
}
5582

83+
// IsExpired verify that the license is expired or not.
5684
func (l *License) IsExpired() bool {
5785
return l.ExpiredAt.Before(time.Now())
5886
}

vo/license_test.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,44 @@ import (
66
)
77

88
func TestLicense_IsOverLimit(t *testing.T) {
9-
t.Run("Return false when the count of member is over the limit.", func(t *testing.T) {
10-
l := NewTrialLicense(6)
9+
t.Run("Return false when the license is OSS.", func(t *testing.T) {
10+
l := NewOSSLicense()
11+
12+
expected := false
13+
if finished := l.IsOverLimit(); finished != expected {
14+
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
15+
}
16+
})
17+
18+
t.Run("Return true when the trial license is over the member limit.", func(t *testing.T) {
19+
l := NewTrialLicense(TrialMemberLimit+1, 0)
1120

1221
expected := true
1322
if finished := l.IsOverLimit(); finished != expected {
1423
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
1524
}
1625
})
1726

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

21-
if finished := tl.IsOverLimit(); finished != false {
22-
t.Fatalf("IsOverLimit = %v, wanted %v", finished, false)
30+
expected := true
31+
if finished := l.IsOverLimit(); finished != expected {
32+
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
2333
}
34+
})
35+
36+
t.Run("Return false when the trial license is less than or equal to the limit.", func(t *testing.T) {
37+
l := NewTrialLicense(TrialMemberLimit, TrialDeploymentLimit)
38+
39+
expected := false
40+
if finished := l.IsOverLimit(); finished != expected {
41+
t.Fatalf("IsOverLimit = %v, wanted %v", finished, expected)
42+
}
43+
})
2444

25-
sl := NewStandardLicense(10, &SigningData{
45+
t.Run("Return true when the standard license is less than the limit.", func(t *testing.T) {
46+
sl := NewStandardLicense(20, &SigningData{
2647
MemberLimit: 20,
2748
ExpiredAt: time.Now(),
2849
})

0 commit comments

Comments
 (0)