feat(captain): gate custom tools
This commit is contained in:
@@ -51,12 +51,13 @@ func (s *CaptainCustomToolCRUDTestSuite) SetupSuite() {
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
account := &model.Account{Name: "CustomToolTestOrg", Locale: "en", Active: true}
|
||||
account := &model.Account{Name: "CustomToolTestOrg", Locale: "en", Active: true, FeatureFlags: `{"custom_tools":true}`}
|
||||
s.Require().NoError(db.Create(account).Error)
|
||||
s.account = account
|
||||
|
||||
toolRepo := repository.NewCaptainCustomToolRepo(db)
|
||||
svc := service.NewCaptainCustomToolService(toolRepo)
|
||||
accountRepo := repository.NewAccountRepo(db)
|
||||
svc := service.NewCaptainCustomToolService(toolRepo, accountRepo)
|
||||
s.handler = NewCaptainCustomToolHandler(svc)
|
||||
|
||||
// Unified router: :id = account_id, :tool_id = tool_id
|
||||
@@ -180,6 +181,24 @@ func (s *CaptainCustomToolCRUDTestSuite) TestCreate_无效accountID返回400() {
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *CaptainCustomToolCRUDTestSuite) TestCreate_FeatureDisabledReturnsForbidden() {
|
||||
disabledAccount := &model.Account{Name: "CustomToolDisabledOrg", Locale: "en", Active: true}
|
||||
s.Require().NoError(s.db.Create(disabledAccount).Error)
|
||||
path := "/api/v1/accounts/" + strconv.FormatUint(uint64(disabledAccount.ID), 10) + "/captain/custom_tools/"
|
||||
|
||||
w := s.makeRequest("POST", path, map[string]interface{}{
|
||||
"custom_tool": map[string]interface{}{
|
||||
"title": "blocked",
|
||||
"endpoint_url": "https://example.com/blocked",
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(s.T(), http.StatusForbidden, w.Code)
|
||||
var resp map[string]interface{}
|
||||
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
assert.Equal(s.T(), "Custom tools are not enabled for this account", resp["error"])
|
||||
}
|
||||
|
||||
// ========== 获取自定义工具测试 ==========
|
||||
|
||||
func (s *CaptainCustomToolCRUDTestSuite) TestGet_成功获取自定义工具() {
|
||||
|
||||
@@ -29,6 +29,9 @@ func (h *CaptainCustomToolHandler) Create(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
|
||||
var req service.CreateCustomToolRequest
|
||||
if err := bindNestedJSONPayload(c, "custom_tool", &req); err != nil {
|
||||
@@ -54,6 +57,9 @@ func (h *CaptainCustomToolHandler) Get(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
id, err := parseUintAnyParam(c, "tool_id", "id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
|
||||
@@ -78,6 +84,9 @@ func (h *CaptainCustomToolHandler) Update(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
id, err := parseUintAnyParam(c, "tool_id", "id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
|
||||
@@ -108,6 +117,9 @@ func (h *CaptainCustomToolHandler) Delete(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
id, err := parseUintAnyParam(c, "tool_id", "id")
|
||||
if err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
|
||||
@@ -131,6 +143,9 @@ func (h *CaptainCustomToolHandler) List(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
|
||||
tools, count, err := h.svc.List(c.Request.Context(), accountID, 0, 1000)
|
||||
if err != nil {
|
||||
@@ -179,6 +194,9 @@ func (h *CaptainCustomToolHandler) TestTool(c *gin.Context) {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
|
||||
return
|
||||
}
|
||||
if !h.ensureCustomToolsEnabled(c, accountID) {
|
||||
return
|
||||
}
|
||||
|
||||
var req service.TestToolRequest
|
||||
if err := bindNestedJSONPayload(c, "custom_tool", &req); err != nil {
|
||||
@@ -196,6 +214,14 @@ func (h *CaptainCustomToolHandler) TestTool(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (h *CaptainCustomToolHandler) ensureCustomToolsEnabled(c *gin.Context, accountID uint) bool {
|
||||
if h.svc.CustomToolsEnabled(c.Request.Context(), accountID) {
|
||||
return true
|
||||
}
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Custom tools are not enabled for this account"})
|
||||
return false
|
||||
}
|
||||
|
||||
func captainCustomToolPayload(tool *model.CaptainCustomTool) gin.H {
|
||||
return gin.H{
|
||||
"id": tool.ID,
|
||||
|
||||
@@ -50,7 +50,7 @@ func (s *CaptainCustomToolTestHandlerTestSuite) SetupSuite() {
|
||||
s.Require().NoError(err)
|
||||
|
||||
// 创建测试账户
|
||||
account := &model.Account{Name: "CustomToolTestOrg", Locale: "en", Active: true}
|
||||
account := &model.Account{Name: "CustomToolTestOrg", Locale: "en", Active: true, FeatureFlags: `{"custom_tools":true}`}
|
||||
s.Require().NoError(db.Create(account).Error)
|
||||
s.account = account
|
||||
|
||||
@@ -70,7 +70,8 @@ func (s *CaptainCustomToolTestHandlerTestSuite) SetupSuite() {
|
||||
|
||||
// 创建 repo + service + handler
|
||||
toolRepo := repository.NewCaptainCustomToolRepo(db)
|
||||
svc := service.NewCaptainCustomToolService(toolRepo)
|
||||
accountRepo := repository.NewAccountRepo(db)
|
||||
svc := service.NewCaptainCustomToolService(toolRepo, accountRepo)
|
||||
svc.SetHTTPClient(fakeCaptainToolHTTPDoer(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{StatusCode: http.StatusCreated, Body: http.NoBody, Header: make(http.Header)}, nil
|
||||
}))
|
||||
|
||||
@@ -46,8 +46,8 @@ func setupCaptainResourceParityTest(t *testing.T) (*gin.Engine, *gorm.DB, *model
|
||||
sqlDB.Close()
|
||||
})
|
||||
|
||||
account := &model.Account{Name: "Captain Account", Locale: "en", Active: true}
|
||||
otherAccount := &model.Account{Name: "Other Account", Locale: "en", Active: true}
|
||||
account := &model.Account{Name: "Captain Account", Locale: "en", Active: true, FeatureFlags: `{"custom_tools":true}`}
|
||||
otherAccount := &model.Account{Name: "Other Account", Locale: "en", Active: true, FeatureFlags: `{"custom_tools":true}`}
|
||||
require.NoError(t, db.Create(account).Error)
|
||||
require.NoError(t, db.Create(otherAccount).Error)
|
||||
assistant := &model.CaptainAssistant{AccountID: account.ID, Name: "Fin", Description: "Support", Config: json.RawMessage(`{}`), Status: model.AssistantStatusActive}
|
||||
@@ -59,7 +59,8 @@ func setupCaptainResourceParityTest(t *testing.T) (*gin.Engine, *gorm.DB, *model
|
||||
scenarioHandler := NewCaptainScenarioHandler(scenarioSvc)
|
||||
|
||||
toolRepo := repository.NewCaptainCustomToolRepo(db)
|
||||
toolSvc := service.NewCaptainCustomToolService(toolRepo)
|
||||
accountRepo := repository.NewAccountRepo(db)
|
||||
toolSvc := service.NewCaptainCustomToolService(toolRepo, accountRepo)
|
||||
toolSvc.SetHTTPClient(fakeCaptainToolHTTPDoer(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{StatusCode: http.StatusCreated, Body: io.NopCloser(strings.NewReader(`{"ok":true}`)), Header: make(http.Header)}, nil
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user