feat(modules): jira scoped a project=DATA + board=33 con seed CLI desde pass
Cambios:
- jiraConfig: nuevo campo BoardID. TestConnection valida que board.location.projectKey
coincide con ProjectKey declarado. Refuse mismatched scopes so a typo in
project_key cannot create issues in the wrong project.
- backend/seed_jira.go: subcomando 'kanban seed-jira-data' lee credenciales
desde pass (jira/anjana/{email,api-token,domain}) e inserta module row con
kind=jira, project_key=DATA, board_id=33, event_filter sensible. Idempotente
(upsert por name). status_map vacio por defecto (operator lo edita por UI).
- main.go: wire del nuevo subcomando.
Requiere KANBAN_MODULE_KEY env var para encriptar/desencriptar config. El
servidor que ejecuta el dispatcher debe usar el mismo valor.
This commit is contained in:
+29
-2
@@ -484,6 +484,7 @@ type jiraConfig struct {
|
||||
Email string `json:"email"`
|
||||
APIToken string `json:"api_token"`
|
||||
ProjectKey string `json:"project_key"`
|
||||
BoardID int `json:"board_id"`
|
||||
StatusMap map[string]string `json:"status_map"`
|
||||
}
|
||||
|
||||
@@ -549,7 +550,33 @@ func (h *jiraHandler) TestConnection(ctx context.Context, m Module) (int, error)
|
||||
return 0, err
|
||||
}
|
||||
status, _, err := h.jiraRequest(ctx, c, http.MethodGet, "/rest/api/3/myself", nil)
|
||||
return status, err
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
// If a board scope is configured, verify the board exists AND lives in
|
||||
// the declared project. Refuse silently-mismatched configurations so a
|
||||
// typo in project_key cannot create issues outside the intended board.
|
||||
if c.BoardID > 0 {
|
||||
bStatus, body, err := h.jiraRequest(ctx, c, http.MethodGet,
|
||||
fmt.Sprintf("/rest/agile/1.0/board/%d", c.BoardID), nil)
|
||||
if err != nil {
|
||||
return bStatus, fmt.Errorf("board %d lookup: %w", c.BoardID, err)
|
||||
}
|
||||
var board struct {
|
||||
Type string `json:"type"`
|
||||
Location struct {
|
||||
ProjectKey string `json:"projectKey"`
|
||||
} `json:"location"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &board); err != nil {
|
||||
return bStatus, fmt.Errorf("decode board %d: %w", c.BoardID, err)
|
||||
}
|
||||
if c.ProjectKey != "" && !strings.EqualFold(board.Location.ProjectKey, c.ProjectKey) {
|
||||
return 0, fmt.Errorf("board %d belongs to project %q, config declares %q",
|
||||
c.BoardID, board.Location.ProjectKey, c.ProjectKey)
|
||||
}
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (h *jiraHandler) Handle(ctx context.Context, db *DB, m Module, ev Event) (int, error) {
|
||||
@@ -586,7 +613,7 @@ func (h *jiraHandler) create(ctx context.Context, db *DB, c jiraConfig, ev Event
|
||||
return h.update(ctx, db, c, ev)
|
||||
}
|
||||
if c.ProjectKey == "" {
|
||||
return 0, fmt.Errorf("project_key required for create")
|
||||
return 0, fmt.Errorf("project_key required for create (configure module before pushing)")
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"fields": map[string]interface{}{
|
||||
|
||||
Reference in New Issue
Block a user