feat: add PATCH /tags/{id}/rules/{then_id} to activate/deactivate rules

- openapi.yaml: new PATCH endpoint with is_active body, returns TagRule
- backend/service: SetRuleActive calls repo.SetActive then returns updated rule
- backend/handler: PatchRule validates body and delegates to service
- backend/router: register PATCH /:tag_id/rules/:then_tag_id
- frontend: TagRuleEditor uses PATCH instead of delete+recreate
- mock: handle PATCH /tags/{id}/rules/{then_id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-05 23:31:12 +03:00
parent 871250345a
commit 21f3acadf0
6 changed files with 97 additions and 6 deletions
+1
View File
@@ -93,6 +93,7 @@ func NewRouter(
tags.GET("/:tag_id/rules", tagHandler.ListRules)
tags.POST("/:tag_id/rules", tagHandler.CreateRule)
tags.PATCH("/:tag_id/rules/:then_tag_id", tagHandler.PatchRule)
tags.DELETE("/:tag_id/rules/:then_tag_id", tagHandler.DeleteRule)
}
+33
View File
@@ -354,6 +354,39 @@ func (h *TagHandler) CreateRule(c *gin.Context) {
respondJSON(c, http.StatusCreated, toTagRuleJSON(*rule))
}
// ---------------------------------------------------------------------------
// PATCH /tags/:tag_id/rules/:then_tag_id
// ---------------------------------------------------------------------------
func (h *TagHandler) PatchRule(c *gin.Context) {
whenTagID, ok := parseTagID(c)
if !ok {
return
}
thenTagID, err := uuid.Parse(c.Param("then_tag_id"))
if err != nil {
respondError(c, domain.ErrValidation)
return
}
var body struct {
IsActive *bool `json:"is_active"`
}
if err := c.ShouldBindJSON(&body); err != nil || body.IsActive == nil {
respondError(c, domain.ErrValidation)
return
}
rule, err := h.tagSvc.SetRuleActive(c.Request.Context(), whenTagID, thenTagID, *body.IsActive)
if err != nil {
respondError(c, err)
return
}
respondJSON(c, http.StatusOK, toTagRuleJSON(*rule))
}
// ---------------------------------------------------------------------------
// DELETE /tags/:tag_id/rules/:then_tag_id
// ---------------------------------------------------------------------------
+17
View File
@@ -191,6 +191,23 @@ func (s *TagService) CreateRule(ctx context.Context, whenTagID, thenTagID uuid.U
})
}
// SetRuleActive toggles a rule's is_active flag and returns the updated rule.
func (s *TagService) SetRuleActive(ctx context.Context, whenTagID, thenTagID uuid.UUID, active bool) (*domain.TagRule, error) {
if err := s.rules.SetActive(ctx, whenTagID, thenTagID, active); err != nil {
return nil, err
}
rules, err := s.rules.ListByTag(ctx, whenTagID)
if err != nil {
return nil, err
}
for _, r := range rules {
if r.ThenTagID == thenTagID {
return &r, nil
}
}
return nil, domain.ErrNotFound
}
// DeleteRule removes a tag rule.
func (s *TagService) DeleteRule(ctx context.Context, whenTagID, thenTagID uuid.UUID) error {
return s.rules.Delete(ctx, whenTagID, thenTagID)