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

Commit 21de3b2

Browse files
author
Noah Lee
authored
Add the productionOnly query parameter to search API (#383)
* Fix the bug of predicate * Fix the order of the index * Set the format UTC at the store pkg * Add the `productionOnly` option
1 parent 582edcb commit 21de3b2

File tree

6 files changed

+129
-135
lines changed

6 files changed

+129
-135
lines changed

internal/interactor/deployment.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ type (
3939
SearchDeploymentsOfUserOptions struct {
4040
ListOptions
4141

42-
Statuses []deployment.Status
43-
Owned bool
44-
From time.Time
45-
To time.Time
42+
Statuses []deployment.Status
43+
Owned bool
44+
ProductionOnly bool
45+
From time.Time
46+
To time.Time
4647
}
4748

4849
// ListInactiveDeploymentsLessThanTimeOptions specifies the optional parameters that

internal/pkg/store/deployment.go

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,56 +35,43 @@ func (s *Store) SearchDeploymentsOfUser(ctx context.Context, u *ent.User, opt *i
3535
return deployment.StatusIn(ss...)
3636
}
3737

38-
if opt.Owned {
39-
return s.c.Deployment.
40-
Query().
41-
Where(
42-
deployment.And(
43-
deployment.UserIDEQ(u.ID),
44-
statusIn(opt.Statuses),
45-
deployment.CreatedAtGTE(opt.From),
46-
deployment.CreatedAtLT(opt.To),
47-
),
48-
).
49-
WithRepo().
50-
WithUser().
51-
Order(ent.Desc(deployment.FieldCreatedAt)).
52-
Offset(offset(opt.Page, opt.PerPage)).
53-
Limit(opt.PerPage).
54-
All(ctx)
55-
}
56-
57-
return s.c.Deployment.
38+
// Build the query searching all deployments under the accessible repositories.
39+
qry := s.c.Deployment.
5840
Query().
5941
Where(func(s *sql.Selector) {
60-
t := sql.Table(perm.Table)
61-
62-
// Join with Perm for Repo.ID
63-
s.Join(t).
64-
On(
65-
s.C(deployment.FieldRepoID),
66-
s.C(perm.FieldRepoID),
67-
).
68-
Where(
69-
sql.EQ(
70-
t.C(perm.FieldUserID),
71-
u.ID,
72-
),
73-
)
42+
p := sql.Table(perm.Table)
43+
44+
s.Join(p).OnP(
45+
sql.And(
46+
sql.ColumnsEQ(p.C(perm.FieldRepoID), s.C(deployment.FieldRepoID)),
47+
sql.EQ(p.C(perm.FieldUserID), u.ID),
48+
),
49+
)
7450
}).
7551
Where(
7652
deployment.And(
7753
statusIn(opt.Statuses),
78-
deployment.CreatedAtGTE(opt.From),
79-
deployment.CreatedAtLT(opt.To),
54+
deployment.CreatedAtGTE(opt.From.UTC()),
55+
deployment.CreatedAtLT(opt.To.UTC()),
8056
),
8157
).
82-
WithRepo().
83-
WithUser().
8458
Order(ent.Desc(deployment.FieldCreatedAt)).
8559
Offset(offset(opt.Page, opt.PerPage)).
8660
Limit(opt.PerPage).
87-
All(ctx)
61+
WithRepo().
62+
WithUser()
63+
64+
// Search only deployments that were triggered by the user.
65+
if opt.Owned {
66+
qry.Where(deployment.UserIDEQ(u.ID))
67+
}
68+
69+
// Search only deployments for production environment.
70+
if opt.ProductionOnly {
71+
qry.Where(deployment.ProductionEnvironment(true))
72+
}
73+
74+
return qry.All(ctx)
8875
}
8976

9077
func (s *Store) ListInactiveDeploymentsLessThanTime(ctx context.Context, opt *i.ListInactiveDeploymentsLessThanTimeOptions) ([]*ent.Deployment, error) {

internal/pkg/store/deployment_test.go

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,62 +23,70 @@ func TestStore_SearchDeployments(t *testing.T) {
2323

2424
ctx := context.Background()
2525

26-
const (
27-
u1 = 1
28-
u2 = 2
29-
r1 = 1
30-
r2 = 2
31-
)
26+
t.Log("User_1 has the permissions for repo_1 and repo_2.")
27+
client.Perm.
28+
Create().
29+
SetUserID(1).
30+
SetRepoID(1).
31+
SaveX(ctx)
3232

3333
client.Perm.
3434
Create().
35-
SetUserID(u1).
36-
SetRepoID(r1).
35+
SetUserID(1).
36+
SetRepoID(2).
3737
SaveX(ctx)
3838

39+
t.Log("Create the deployments under the repo_1.")
3940
client.Deployment.Create().
4041
SetType(deployment.TypeBranch).
4142
SetNumber(1).
4243
SetRef("main").
4344
SetEnv("local").
44-
SetUserID(u1).
45-
SetRepoID(r1).
45+
SetStatus(deployment.StatusCreated).
46+
SetProductionEnvironment(true).
47+
SetUserID(1).
48+
SetRepoID(1).
49+
SaveX(ctx)
50+
51+
t.Log("Create the deployments under the repo_2.")
52+
client.Deployment.Create().
53+
SetType(deployment.TypeBranch).
54+
SetNumber(1).
55+
SetRef("main").
56+
SetEnv("dev").
57+
SetUserID(1).
58+
SetRepoID(2).
4659
SaveX(ctx)
4760

4861
client.Deployment.Create().
4962
SetType(deployment.TypeBranch).
5063
SetNumber(2).
5164
SetRef("main").
5265
SetEnv("dev").
53-
SetUserID(u2).
54-
SetRepoID(r1).
66+
SetUserID(2).
67+
SetRepoID(2).
5568
SaveX(ctx)
5669

70+
t.Log("Create the deployments under the repo_3.")
5771
client.Deployment.Create().
5872
SetType(deployment.TypeBranch).
59-
SetNumber(3).
73+
SetNumber(1).
6074
SetRef("main").
6175
SetEnv("dev").
62-
SetUserID(u2).
63-
SetRepoID(r1).
76+
SetUserID(2).
77+
SetRepoID(3).
6478
SetStatus(deployment.StatusCreated).
6579
SaveX(ctx)
6680

67-
t.Run("u1 searchs all deployments of r1.", func(t *testing.T) {
68-
const (
69-
owned = false
70-
page = 1
71-
perPage = 30
72-
)
73-
81+
t.Run("Returns all deployments under the accessible repositories.", func(t *testing.T) {
7482
store := NewStore(client)
7583

7684
res, err := store.SearchDeploymentsOfUser(ctx,
77-
&ent.User{ID: u1},
85+
&ent.User{ID: 1},
7886
&i.SearchDeploymentsOfUserOptions{
79-
ListOptions: i.ListOptions{Page: page, PerPage: perPage},
87+
ListOptions: i.ListOptions{Page: 1, PerPage: 30},
8088
Statuses: []deployment.Status{},
81-
Owned: owned,
89+
Owned: false,
8290
From: time.Now().UTC().Add(-time.Minute),
8391
To: time.Now().UTC(),
8492
})
@@ -92,21 +100,15 @@ func TestStore_SearchDeployments(t *testing.T) {
92100
}
93101
})
94102

95-
t.Run("u1 searchs waiting deployments of r1.", func(t *testing.T) {
96-
const (
97-
owned = false
98-
page = 1
99-
perPage = 30
100-
)
101-
103+
t.Run("Returns all deployment triggered by myself.", func(t *testing.T) {
102104
store := NewStore(client)
103105

104106
res, err := store.SearchDeploymentsOfUser(ctx,
105-
&ent.User{ID: u1},
107+
&ent.User{ID: 1},
106108
&i.SearchDeploymentsOfUserOptions{
107-
ListOptions: i.ListOptions{Page: page, PerPage: perPage},
108-
Statuses: []deployment.Status{deployment.StatusWaiting},
109-
Owned: owned,
109+
ListOptions: i.ListOptions{Page: 1, PerPage: 30},
110+
Statuses: []deployment.Status{},
111+
Owned: true,
110112
From: time.Now().UTC().Add(-time.Minute),
111113
To: time.Now().UTC(),
112114
})
@@ -116,25 +118,41 @@ func TestStore_SearchDeployments(t *testing.T) {
116118

117119
expected := 2
118120
if len(res) != expected {
119-
t.Fatalf("SearchDeployments = %v, wanted %v", len(res), expected)
121+
t.Fatalf("SearchDeployments = %v, wanted %v", res, expected)
120122
}
121123
})
122124

123-
t.Run("u1 searchs owned deployments of r1.", func(t *testing.T) {
124-
const (
125-
owned = true
126-
page = 1
127-
perPage = 30
128-
)
125+
t.Run("Returns deployments which are 'created'.", func(t *testing.T) {
126+
store := NewStore(client)
129127

128+
res, err := store.SearchDeploymentsOfUser(ctx,
129+
&ent.User{ID: 1},
130+
&i.SearchDeploymentsOfUserOptions{
131+
ListOptions: i.ListOptions{Page: 1, PerPage: 30},
132+
Statuses: []deployment.Status{deployment.StatusCreated},
133+
Owned: false,
134+
From: time.Now().UTC().Add(-time.Minute),
135+
To: time.Now().UTC(),
136+
})
137+
if err != nil {
138+
t.Fatalf("SearchDeployments return an error: %s", err)
139+
}
140+
141+
expected := 1
142+
if len(res) != expected {
143+
t.Fatalf("SearchDeployments = %v, wanted %v", len(res), expected)
144+
}
145+
})
146+
147+
t.Run("Returns deployments for production environment.", func(t *testing.T) {
130148
store := NewStore(client)
131149

132150
res, err := store.SearchDeploymentsOfUser(ctx,
133-
&ent.User{ID: u1},
151+
&ent.User{ID: 1},
134152
&i.SearchDeploymentsOfUserOptions{
135-
ListOptions: i.ListOptions{Page: page, PerPage: perPage},
136-
Statuses: []deployment.Status{},
137-
Owned: owned,
153+
ListOptions: i.ListOptions{Page: 1, PerPage: 30},
154+
Statuses: []deployment.Status{deployment.StatusCreated},
155+
Owned: false,
138156
From: time.Now().UTC().Add(-time.Minute),
139157
To: time.Now().UTC(),
140158
})
@@ -144,7 +162,7 @@ func TestStore_SearchDeployments(t *testing.T) {
144162

145163
expected := 1
146164
if len(res) != expected {
147-
t.Fatalf("SearchDeployments = %v, wanted %v", res, expected)
165+
t.Fatalf("SearchDeployments = %v, wanted %v", len(res), expected)
148166
}
149167
})
150168
}

internal/server/api/v1/search/search.go

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -38,68 +38,54 @@ func (s *Search) SearchDeployments(c *gin.Context) {
3838
ctx := c.Request.Context()
3939

4040
var (
41-
statuses = c.DefaultQuery("statuses", "")
42-
owned = c.DefaultQuery("owned", "true")
43-
from = c.DefaultQuery("from", time.Now().Add(-activeDuration).Format(time.RFC3339))
44-
to = c.DefaultQuery("to", time.Now().Format(time.RFC3339))
45-
page = c.DefaultQuery("page", "1")
46-
perPage = c.DefaultQuery("per_page", "30")
47-
)
48-
49-
var (
50-
ss = make([]deployment.Status, 0)
51-
o bool
52-
f time.Time
53-
t time.Time
54-
p int
55-
pp int
56-
err error
41+
statuses = []deployment.Status{}
42+
owned bool
43+
productionOnly bool
44+
from, to time.Time
45+
page, perPage int
46+
err error
5747
)
5848

5949
// Validate query parameters.
60-
for _, st := range strings.Split(statuses, ",") {
61-
if st != "" {
62-
ss = append(ss, deployment.Status(st))
50+
for _, s := range strings.Split(c.DefaultQuery("statuses", ""), ",") {
51+
if s != "" {
52+
statuses = append(statuses, deployment.Status(s))
6353
}
6454
}
6555

66-
if o, err = strconv.ParseBool(owned); err != nil {
67-
gb.ResponseWithError(
68-
c,
69-
e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "The owned must be boolean.", err),
70-
)
56+
if owned, err = strconv.ParseBool(c.DefaultQuery("owned", "true")); err != nil {
57+
gb.ResponseWithError(c, e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "The owned must be boolean.", err))
58+
return
59+
}
60+
61+
if productionOnly, err = strconv.ParseBool(c.DefaultQuery("production_only", "false")); err != nil {
62+
gb.ResponseWithError(c, e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "The production must be boolean.", err))
7163
return
7264
}
7365

74-
if f, err = time.Parse(time.RFC3339, from); err != nil {
66+
if from, err = time.Parse(time.RFC3339, c.DefaultQuery("from", time.Now().Add(-activeDuration).Format(time.RFC3339))); err != nil {
7567
gb.ResponseWithError(
7668
c,
7769
e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "Invalid format of \"from\" parameter, RFC3339 format only.", err),
7870
)
7971
return
8072
}
8173

82-
if t, err = time.Parse(time.RFC3339, to); err != nil {
74+
if to, err = time.Parse(time.RFC3339, c.DefaultQuery("to", time.Now().Format(time.RFC3339))); err != nil {
8375
gb.ResponseWithError(
8476
c,
8577
e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "Invalid format of \"to\" parameter, RFC3339 format only.", err),
8678
)
8779
return
8880
}
8981

90-
if p, err = strconv.Atoi(page); err != nil {
91-
gb.ResponseWithError(
92-
c,
93-
e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "Invalid format of \"page\" parameter.", err),
94-
)
82+
if page, err = strconv.Atoi(c.DefaultQuery("page", "1")); err != nil {
83+
gb.ResponseWithError(c, e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "The page must be number.", err))
9584
return
9685
}
9786

98-
if pp, err = strconv.Atoi(perPage); err != nil {
99-
gb.ResponseWithError(
100-
c,
101-
e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "Invalid format of \"per_page\" parameter.", err),
102-
)
87+
if perPage, err = strconv.Atoi(c.DefaultQuery("per_page", "1")); err != nil {
88+
gb.ResponseWithError(c, e.NewErrorWithMessage(e.ErrorCodeParameterInvalid, "The per_page must be number.", err))
10389
return
10490
}
10591

@@ -112,11 +98,12 @@ func (s *Search) SearchDeployments(c *gin.Context) {
11298
u := v.(*ent.User)
11399

114100
if ds, err = s.i.SearchDeploymentsOfUser(ctx, u, &i.SearchDeploymentsOfUserOptions{
115-
ListOptions: i.ListOptions{Page: p, PerPage: pp},
116-
Statuses: ss,
117-
Owned: o,
118-
From: f.UTC(),
119-
To: t.UTC(),
101+
ListOptions: i.ListOptions{Page: page, PerPage: perPage},
102+
Statuses: statuses,
103+
Owned: owned,
104+
ProductionOnly: productionOnly,
105+
From: from,
106+
To: to,
120107
}); err != nil {
121108
s.log.Check(gb.GetZapLogLevel(err), "Failed to search deployments.").Write(zap.Error(err))
122109
gb.ResponseWithError(c, err)

0 commit comments

Comments
 (0)