Skip to content

Commit

Permalink
- Add API `DELETE /v1/products/{product}/modules/{module}/settings/{s…
Browse files Browse the repository at this point in the history
…etting}:cleanup` that cleanup all rules, users and groups on the setting.

- Add API `DELETE /v1/products/{product}/labels/{label}:cleanup` that cleanup all rules, users and groups on the label.
  • Loading branch information
zensh committed Jun 8, 2020
1 parent d1b6279 commit e44f982
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file starting fro
This project adheres to [Semantic Versioning](http://semver.org/).

-----
## [1.5.0] - 2020-06-08

**Change:**

- Add API `DELETE /v1/products/{product}/modules/{module}/settings/{setting}:cleanup` that cleanup all rules, users and groups on the setting.
- Add API `DELETE /v1/products/{product}/labels/{label}:cleanup` that cleanup all rules, users and groups on the label.

## [1.4.0] - 2020-05-27

**Change:**
Expand Down
14 changes: 14 additions & 0 deletions src/api/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ func (a *Label) Recall(ctx *gear.Context) error {
return ctx.OkJSON(res)
}

// Cleanup ..
func (a *Label) Cleanup(ctx *gear.Context) error {
req := tpl.ProductLabelURL{}
if err := ctx.ParseURL(&req); err != nil {
return err
}

res, err := a.blls.Label.Cleanup(ctx, req.Product, req.Label)
if err != nil {
return err
}
return ctx.OkJSON(res)
}

// CreateRule ..
func (a *Label) CreateRule(ctx *gear.Context) error {
req := tpl.ProductLabelURL{}
Expand Down
116 changes: 116 additions & 0 deletions src/api/label_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,102 @@ func TestLabelAPIs(t *testing.T) {
})
})

t.Run(`"PUT /v1/products/:product/labels/:label+:cleanup"`, func(t *testing.T) {
label, err := createLabel(tt, product.Name)
assert.Nil(t, err)

users, err := createUsers(tt, 3)
assert.Nil(t, err)

group, err := createGroup(tt)
assert.Nil(t, err)

t.Run("should work", func(t *testing.T) {
assert := assert.New(t)

res, err := request.Post(fmt.Sprintf("%s/v1/products/%s/labels/%s:assign", tt.Host, product.Name, label.Name)).
Set("Content-Type", "application/json").
Send(tpl.UsersGroupsBody{
Users: schema.GetUsersUID(users),
Groups: []string{group.UID},
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)
res.Content() // close http client

res, err = request.Post(fmt.Sprintf("%s/v1/products/%s/labels/%s/rules", tt.Host, product.Name, label.Name)).
Set("Content-Type", "application/json").
Send(map[string]interface{}{
"kind": "userPercent",
"rule": map[string]interface{}{
"value": 100,
},
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

var count int64
_, err = tt.DB.ScanVal(&count, "select count(*) from `user_label` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(3), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `group_label` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `label_rule` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

res, err = request.Delete(fmt.Sprintf("%s/v1/products/%s/labels/%s:cleanup", tt.Host, product.Name, label.Name)).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

json := tpl.BoolRes{}
res.JSON(&json)
assert.True(json.Result)

l := label
_, err = tt.DB.ScanStruct(&l, "select * from `urbs_label` where `id` = ? limit 1", label.ID)
assert.Nil(err)
assert.Equal(int64(0), l.Status)

_, err = tt.DB.ScanVal(&count, "select count(*) from `user_label` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `group_label` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `label_rule` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(0), count)
})

t.Run("should work idempotent", func(t *testing.T) {
assert := assert.New(t)

res, err := request.Delete(fmt.Sprintf("%s/v1/products/%s/labels/%s:cleanup", tt.Host, product.Name, label.Name)).
End()
assert.Nil(err)

assert.Equal(200, res.StatusCode)

json := tpl.BoolRes{}
res.JSON(&json)
assert.True(json.Result)

l := label
_, err = tt.DB.ScanStruct(&l, "select * from `urbs_label` where `id` = ? limit 1", label.ID)
assert.Nil(err)
assert.Equal(int64(0), l.Status)
})
})

t.Run(`"PUT /v1/products/:product/labels/:label+:offline"`, func(t *testing.T) {
label, err := createLabel(tt, product.Name)
assert.Nil(t, err)
Expand All @@ -513,6 +609,18 @@ func TestLabelAPIs(t *testing.T) {
assert.Equal(200, res.StatusCode)
res.Content() // close http client

res, err = request.Post(fmt.Sprintf("%s/v1/products/%s/labels/%s/rules", tt.Host, product.Name, label.Name)).
Set("Content-Type", "application/json").
Send(map[string]interface{}{
"kind": "userPercent",
"rule": map[string]interface{}{
"value": 100,
},
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

var count int64
_, err = tt.DB.ScanVal(&count, "select count(*) from `user_label` where `label_id` = ?", label.ID)
assert.Nil(err)
Expand All @@ -522,6 +630,10 @@ func TestLabelAPIs(t *testing.T) {
assert.Nil(err)
assert.Equal(int64(1), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `label_rule` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

res, err = request.Put(fmt.Sprintf("%s/v1/products/%s/labels/%s:offline", tt.Host, product.Name, label.Name)).
End()
assert.Nil(err)
Expand All @@ -544,6 +656,10 @@ func TestLabelAPIs(t *testing.T) {
_, err = tt.DB.ScanVal(&count, "select count(*) from `group_label` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `label_rule` where `label_id` = ?", label.ID)
assert.Nil(err)
assert.Equal(int64(0), count)
})

t.Run("should work idempotent", func(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions src/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ func newRouters(apis *APIs) []*gear.Router {
routerV1.Post("/products/:product/modules/:module/settings/:setting+:assign", apis.Setting.Assign)
// 批量撤销对用户或群组设置的产品功能模块配置项
routerV1.Post("/products/:product/modules/:module/settings/:setting+:recall", apis.Setting.Recall)
// 清除指定产品功能模块配置项下所有的用户、群组和百分比规则
routerV1.Delete("/products/:product/modules/:module/settings/:setting+:cleanup", apis.Setting.Cleanup)
// 创建指定产品功能模块配置项的灰度发布规则
routerV1.Post("/products/:product/modules/:module/settings/:setting/rules", apis.Setting.CreateRule)
// 更新指定产品功能模块配置项的指定灰度发布规则
Expand Down Expand Up @@ -172,6 +174,8 @@ func newRouters(apis *APIs) []*gear.Router {
routerV1.Post("/products/:product/labels/:label+:assign", apis.Label.Assign)
// 批量撤销对用户或群组设置的产品环境标签
routerV1.Post("/products/:product/labels/:label+:recall", apis.Label.Recall)
// 清除产品环境标签下所有的用户、群组和百分比规则
routerV1.Delete("/products/:product/labels/:label+:cleanup", apis.Label.Cleanup)
// 创建指定产品环境标签的灰度发布规则
routerV1.Post("/products/:product/labels/:label/rules", apis.Label.CreateRule)
// 读取指定产品环境标签的灰度发布规则列表
Expand Down
14 changes: 14 additions & 0 deletions src/api/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ func (a *Setting) Recall(ctx *gear.Context) error {
return ctx.OkJSON(res)
}

// Cleanup ..
func (a *Setting) Cleanup(ctx *gear.Context) error {
req := tpl.ProductModuleSettingURL{}
if err := ctx.ParseURL(&req); err != nil {
return err
}

res, err := a.blls.Setting.Cleanup(ctx, req.Product, req.Module, req.Setting)
if err != nil {
return err
}
return ctx.OkJSON(res)
}

// CreateRule ..
func (a *Setting) CreateRule(ctx *gear.Context) error {
req := tpl.ProductModuleSettingURL{}
Expand Down
124 changes: 124 additions & 0 deletions src/api/setting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,109 @@ func TestSettingAPIs(t *testing.T) {
})
})

t.Run(`"PUT /v1/products/:product/modules/:module/settings/:setting+:cleanup"`, func(t *testing.T) {
product, err := createProduct(tt)
assert.Nil(t, err)

module, err := createModule(tt, product.Name)
assert.Nil(t, err)

setting, err := createSetting(tt, product.Name, module.Name, "x", "y")
assert.Nil(t, err)

users, err := createUsers(tt, 3)
assert.Nil(t, err)

group, err := createGroup(tt)
assert.Nil(t, err)

t.Run("should work", func(t *testing.T) {
assert := assert.New(t)

res, err := request.Post(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s:assign", tt.Host, product.Name, module.Name, setting.Name)).
Set("Content-Type", "application/json").
Send(tpl.UsersGroupsBody{
Users: schema.GetUsersUID(users),
Groups: []string{group.UID},
Value: "x",
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

json := tpl.SettingReleaseInfoRes{}
res.JSON(&json)
result := json.Result
assert.Equal(int64(1), result.Release)
assert.Equal("x", result.Value)

res, err = request.Post(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s/rules", tt.Host, product.Name, module.Name, setting.Name)).
Set("Content-Type", "application/json").
Send(map[string]interface{}{
"kind": "userPercent",
"value": "y",
"rule": map[string]interface{}{
"value": 100,
},
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

var count int64
_, err = tt.DB.ScanVal(&count, "select count(*) from `user_setting` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(3), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `group_setting` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `setting_rule` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

res, err = request.Delete(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s:cleanup", tt.Host, product.Name, module.Name, setting.Name)).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

json2 := tpl.BoolRes{}
res.JSON(&json2)
assert.True(json2.Result)

time.Sleep(time.Millisecond * 100)
_, err = tt.DB.ScanVal(&count, "select count(*) from `user_setting` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `group_setting` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `setting_rule` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

s := setting
_, err = tt.DB.ScanStruct(&s, "select * from `urbs_setting` where `id` = ? limit 1", s.ID)
assert.Equal(int64(0), s.Status)
})

t.Run("should work idempotent", func(t *testing.T) {
assert := assert.New(t)

res, err := request.Delete(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s:cleanup", tt.Host, product.Name, module.Name, setting.Name)).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

json := tpl.BoolRes{}
res.JSON(&json)
assert.True(json.Result)
})
})

t.Run(`"PUT /v1/products/:product/modules/:module/settings/:setting+:offline"`, func(t *testing.T) {
product, err := createProduct(tt)
assert.Nil(t, err)
Expand Down Expand Up @@ -625,6 +728,19 @@ func TestSettingAPIs(t *testing.T) {
assert.Equal(int64(1), result.Release)
assert.Equal("x", result.Value)

res, err = request.Post(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s/rules", tt.Host, product.Name, module.Name, setting.Name)).
Set("Content-Type", "application/json").
Send(map[string]interface{}{
"kind": "userPercent",
"value": "y",
"rule": map[string]interface{}{
"value": 100,
},
}).
End()
assert.Nil(err)
assert.Equal(200, res.StatusCode)

var count int64
_, err = tt.DB.ScanVal(&count, "select count(*) from `user_setting` where `setting_id` = ?", setting.ID)
assert.Nil(err)
Expand All @@ -634,6 +750,10 @@ func TestSettingAPIs(t *testing.T) {
assert.Nil(err)
assert.Equal(int64(1), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `setting_rule` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(1), count)

res, err = request.Put(fmt.Sprintf("%s/v1/products/%s/modules/%s/settings/%s:offline", tt.Host, product.Name, module.Name, setting.Name)).
End()
assert.Nil(err)
Expand All @@ -652,6 +772,10 @@ func TestSettingAPIs(t *testing.T) {
assert.Nil(err)
assert.Equal(int64(0), count)

_, err = tt.DB.ScanVal(&count, "select count(*) from `setting_rule` where `setting_id` = ?", setting.ID)
assert.Nil(err)
assert.Equal(int64(0), count)

assert.Nil(setting.OfflineAt)
s := setting
_, err = tt.DB.ScanStruct(&s, "select * from `urbs_setting` where `id` = ? limit 1", s.ID)
Expand Down
20 changes: 20 additions & 0 deletions src/bll/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,26 @@ func (b *Label) Recall(ctx context.Context, productName, labelName string, relea
return res, nil
}

// Cleanup ...
func (b *Label) Cleanup(ctx context.Context, productName, labelName string) (*tpl.BoolRes, error) {
productID, err := b.ms.Product.AcquireID(ctx, productName)
if err != nil {
return nil, err
}

label, err := b.ms.Label.Acquire(ctx, productID, labelName)
if err != nil {
return nil, err
}

res := &tpl.BoolRes{Result: false}
if err = b.ms.Label.Cleanup(ctx, label.ID); err != nil {
return nil, err
}
res.Result = true
return res, nil
}

// CreateRule ...
func (b *Label) CreateRule(ctx context.Context, productName, labelName string, body tpl.LabelRuleBody) (*tpl.LabelRuleInfoRes, error) {
productID, err := b.ms.Product.AcquireID(ctx, productName)
Expand Down
Loading

0 comments on commit e44f982

Please sign in to comment.