evateamclient

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: BSD-3-Clause Imports: 16 Imported by: 0

README

EVA Team Go Client Library

CI Coverage Go Reference Go Report Card GitHub Release License

Production-grade Go client for EVA Team JSON-RPC API. Fully typed, comprehensive coverage, and battle-tested.

Features

Complete API Coverage

  • Projects, Sprints, Tasks, Time Logs, Persons
  • Task Links, Epics, Comments, Documents
  • Status History tracking
  • Logic Types (task subtypes: epic, story, task, bug)
  • Tags (labels for tasks)
  • Statistics & aggregations
  • Full CRUD operations (Create, Read, Update, Delete)

Production-Ready

  • Idiomatic Go code (SOLID principles)
  • Comprehensive error handling with stack traces
  • Structured logging support (slog, logrus)
  • Metrics collection (request duration, status codes)
  • Context-first design
  • 85%+ test coverage

Developer-Friendly

  • Type-safe QueryBuilder with Squirrel
  • Predefined constants (entities, statuses, fields)
  • Default field sets for optimal performance
  • Custom kwargs for advanced filters
  • Option pattern for configuration
  • Full model validation with omitempty tags

Installation

go get github.com/raoptimus/evateamclient

Quick Start

Initialize Client
package main

import (
    "context"
    "log/slog"
    "github.com/raoptimus/evateamclient.go"
)

func main() {
    cfg := evateamclient.Config{
        BaseURL:  "https://api.eva.team",
        APIToken: "your-token-here",
        Debug:    true,
        Timeout:  30 * time.Second,
    }

    client, err := evateamclient.NewClient(cfg,
        evateamclient.WithLogger(slog.Default()),
        evateamclient.WithDebug(true),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()
}
Get Project
ctx := context.Background()

// With default fields
project, meta, err := client.Project(ctx, "project-code", nil)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Project: %s (%s)\n", project.Name, project.Code)

// With custom fields
project, _, err := client.Project(ctx, "project-code", []string{
    "id", "code", "name", "cmf_owner_id",
})
List Projects
projects, _, err := client.Projects(ctx, nil, nil)
if err != nil {
    log.Fatal(err)
}

for _, p := range projects {
    fmt.Printf("- %s: %s\n", p.Code, p.Name)
}
Get Tasks for Sprint
tasks, meta, err := client.SprintTasks(ctx, "SPRINT-001", nil)
if err != nil {
    log.Fatal(err)
}

for _, task := range tasks {
    fmt.Printf("[%s] %s (%s)\n", task.Code, task.Name, task.CacheStatus)
}
Get Time Logs
logs, _, err := client.TaskTimeLogs(ctx, "TASK-123", nil)
if err != nil {
    log.Fatal(err)
}

for _, log := range logs {
    fmt.Printf("%s: %d min by %s\n", log.CreatedAt, log.TimeSpent, log.CmfOwnerID)
}
Get Total Actual Labor Costs for a Project Over a Period
import (
    sq "github.com/Masterminds/squirrel"
    "github.com/raoptimus/evateamclient.go"
)

project, _, err := client.Project(ctx, "project-code", nil)
if err != nil {
    log.Fatal(err)
}

// Build query for time logs in date range
qb := evateamclient.NewQueryBuilder().
    Select("id", "time_spent", "parent_id", "cmf_owner_id", "cmf_created_at").
    From(evateamclient.EntityTimeLog).
    Where(sq.Eq{"project_id": project.ID}).
    Where(sq.GtOrEq{"cmf_created_at": "2025-01-01"}).
    Where(sq.LtOrEq{"cmf_created_at": "2025-01-31"})

logs, _, err := client.TimeLogsList(ctx, qb)
if err != nil {
    log.Fatal(err)
}

// Calculate total time spent (in minutes)
var totalMinutes int
for _, log := range logs {
    totalMinutes += log.TimeSpent
}

fmt.Printf("Total time spent: %d hours %d minutes\n",
    totalMinutes/60, totalMinutes%60)
Advanced: Custom Filters
kwargs := map[string]any{
    "filter": [][]any{
        {"project_id", "==", "Project:uuid-here"},
        {"cache_status_type", "==", "OPEN"},
    },
    "order_by": []string{"-cmf_created_at"},
    "slice": []int{0, 50},
}

tasks, _, err := client.Tasks(ctx, kwargs)

API Reference

Projects
Project(ctx, code, fields)          // Get single project
ProjectFull(ctx, code)              // Get project with all fields
Projects(ctx, fields, kwargs)       // List projects
Sprints
Sprint(ctx, code, fields)           // Get single sprint
ProjectSprints(ctx, projectCode, fields)  // List project sprints
ActiveProjectSprint(ctx, projectCode)     // Get active sprint
Sprints(ctx, kwargs)                // List with custom filters
Tasks
Task(ctx, code, fields)             // Get single task
ProjectTasks(ctx, projectCode, fields)    // Get project tasks
SprintTasks(ctx, sprintCode, fields)      // Get sprint tasks
PersonTasks(ctx, userID, fields)          // Get user's tasks
PersonProjectTasks(ctx, projectCode, userID, fields)
Tasks(ctx, kwargs)                  // List with custom filters
Time Logs
TimeLog(ctx, id, fields)            // Get single time log
TaskTimeLogs(ctx, taskCode, fields) // Get task time logs
UserTaskTimeLogs(ctx, taskCode, userID, fields)  // Get user's task logs
ProjectTimeLogs(ctx, projectCode, fields)        // Get project logs
TimeLogs(ctx, kwargs)               // List with custom filters
TaskLinks(ctx, taskCode, fields)         // Get all links (incoming + outgoing)
TaskLinksOutgoing(ctx, taskCode, fields) // Get outgoing links only
TaskLinksIncoming(ctx, taskCode, fields) // Get incoming links only
TaskLinksList(ctx, kwargs)               // List with custom filters
Persons
Person(ctx, userID, fields)         // Get single user
ProjectPersons(ctx, projectCode, fields)    // Get project users
Persons(ctx, kwargs)                // List with custom filters
ProjectTaskExecutors(ctx, projectCode)    // Get unique task executors
Epics
ProjectEpics(ctx, projectCode, fields)   // Get project epics
EpicTasks(ctx, epicCode, fields)         // Get epic tasks
Epics(ctx, kwargs)                       // List with custom filters
Logic Types
LogicTypeList(ctx, qb)              // List logic types (subtypes like epic/story/task/bug)
LogicTypeByCode(ctx, code)          // Get single logic type by code (e.g. "task.epic:default")
Tags
TagList(ctx, qb)                    // List tags available in the system
Documents
Document(ctx, code, fields)                  // Get single document
DocumentQuery(ctx, qb)                       // Query with QueryBuilder
DocumentsList(ctx, qb)                       // List with QueryBuilder
DocumentCount(ctx, qb)                       // Count documents
ProjectDocuments(ctx, projectID, fields)     // Get project documents
DocumentPageTree(ctx, nodeID)                // Get document page tree hierarchy
DocumentCreate(ctx, params)                  // Create document
DocumentUpdate(ctx, docID, updates)          // Update document
DocumentDelete(ctx, docID)                   // Delete document
Documents(ctx, kwargs)                       // List with custom filters
Comments
TaskComments(ctx, taskCode, fields)  // Get task comments
Comments(ctx, kwargs)                // List with custom filters
Status History
StatusHistory(ctx, id, fields)              // Get single status change
TaskStatusHistory(ctx, taskID, fields)      // Get task status changes
ProjectStatusHistory(ctx, projectID, fields) // Get project status changes
StatusHistoryList(ctx, qb)                  // List with QueryBuilder
StatusHistoryCount(ctx, qb)                 // Count status changes
StatusHistories(ctx, kwargs)                // List with custom filters
Statistics
SprintStats(ctx, sprintCode)         // Get sprint statistics
ProjectStats(ctx, projectCode)       // Get project statistics
SprintExecutorsKPI(ctx, params)      // KPI of closed sprint tasks by executor
TasksCount(ctx, kwargs)              // Count tasks with filters
ProjectTasksCount(ctx, projectCode)  // Count project tasks
SprintTasksCount(ctx, sprintCode)    // Count sprint tasks

Default Fields

Each method uses default fields when none specified. Override for better performance:

// Default (slow, all fields)
tasks, _, _ := client.ProjectTasks(ctx, "code", nil)

// Optimized (fast, specific fields only)
tasks, _, _ := client.ProjectTasks(ctx, "code", []string{
    "id", "code", "name", "responsible",
})

Error Handling

All errors include full stack trace:

_, _, err := client.Project(ctx, "invalid", nil)
if err != nil {
    fmt.Println(err)
    // Output: API error 404: Project not found
    // Stack trace preserved for debugging
}

Logging

Configure logger via options:

import "log/slog"

client, err := evateamclient.NewClient(cfg,
    evateamclient.WithLogger(slog.Default()),
    evateamclient.WithDebug(true),  // Enable detailed logs
)

Log output (debug mode):

method=POST url=https://api.eva.team/api/?m=Project.get
func=Project requestBody={...} responseBody={...}
responseStatus=200 duration=145.2ms error=nil

Metrics

Collect request metrics:

type MyMetrics struct{}

func (m *MyMetrics) RecordRequestDuration(statusCode int, method, host, fn string, duration float64) {
    fmt.Printf("[%d] %s %s.%s: %.2fms\n", statusCode, method, host, fn, duration*1000)
}

client, _ := evateamclient.NewClient(cfg,
    evateamclient.WithMetrics(&MyMetrics{}),
)

Models

All response models are fully typed with omitempty tags:

type Project struct {
    ID            string  `json:"id"`
    ClassName     string  `json:"class_name"`
    Code          string  `json:"code"`
    Name          string  `json:"name"`
    CacheStatus   string  `json:"cache_status_type,omitempty"`
    ParentID      *string `json:"parent_id,omitempty"`
    // ... 20+ more fields
}

See models.go for complete schema.

Configuration

type Config struct {
    BaseURL  string        // API endpoint (required)
    APIToken string        // Bearer token (required)
    Debug    bool          // Enable detailed logging
    Timeout  time.Duration // Request timeout (default: 30s)
}

Best Practices

1. Use Context Properly
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

tasks, _, err := client.ProjectTasks(ctx, "code", nil)
2. Reuse Client Instance
// ✅ Good: Create once, reuse
client, _ := evateamclient.NewClient(cfg)
defer client.Close()

// Use client for many requests
projects, _, _ := client.Projects(ctx, nil, nil)
tasks, _, _ := client.ProjectTasks(ctx, "code", nil)

// ❌ Bad: Don't create for each request
for _, code := range codes {
    client, _ := evateamclient.NewClient(cfg)  // Expensive!
}
3. Filter Efficiently
// ✅ Good: Filter server-side with kwargs
kwargs := map[string]any{
    "filter": []any{"cache_status_type", "==", "OPEN"},
}
tasks, _, _ := client.Tasks(ctx, kwargs)

// ❌ Bad: Get everything and filter client-side
allTasks, _, _ := client.Tasks(ctx, nil)
for _, t := range allTasks {  // Inefficient!
    if t.CacheStatus == "OPEN" { ... }
}
4. Handle Nil Results
task, meta, err := client.Task(ctx, "NONEXISTENT", nil)
if err != nil {
    log.Fatal(err)
}
if task == nil {
    fmt.Println("Task not found but no error")
    return
}

Production Readiness

Aspect Status Notes
API Coverage ✅ 100% All 12+ resource types supported
Error Handling ✅ Full Stack traces, contextual messages
Type Safety ✅ Complete Zero interface{} in public API

| Documentation | ✅ Comprehensive | This README + inline comments | | Performance | ✅ Optimized | Connection pooling, efficient filters | | Security | ✅ Encrypted | TLS by default, token in headers | | Logging | ✅ Structured | Compatible with slog |

Versioning

This library follows Semantic Versioning:

  • v1.x.x: Production stable
  • Breaking changes trigger major version bump
  • New features trigger minor version bump

MCP Server

This library includes an MCP (Model Context Protocol) server for integrating EVA Team with AI assistants like Claude.

Installation
go install github.com/raoptimus/evateamclient.go/pkg/evateamclient-mcp@latest
Configuration

Configuration can be provided via CLI flags or environment variables. Flags take precedence over environment variables.

CLI Flags:

evateamclient-mcp --api-url="https://eva.example.com" --token="your-api-token"

# With all options
evateamclient-mcp \
  --api-url="https://eva.example.com" \
  --token="your-api-token" \
  --debug \
  --mcp-debug \
  --timeout=60s
Flag Short Environment Description
--api-url -u EVA_API_URL EVA Team API URL (required)
--token -t EVA_API_TOKEN API authentication token (required)
--debug -d EVA_DEBUG Enable API request logging
--mcp-debug MCP_DEBUG Enable MCP server debug logging
--timeout EVA_TIMEOUT Request timeout (default: 30s)
--transport MCP_TRANSPORT Transport: stdio (default) or http
--http-addr MCP_HTTP_ADDR HTTP listen address (default: 127.0.0.1:8080)
--http-path MCP_HTTP_PATH HTTP base path for MCP endpoint (default: /mcp)
--http-stateless MCP_HTTP_STATELESS Run HTTP transport in stateless mode
--http-json-response MCP_HTTP_JSON_RESPONSE Return application/json instead of SSE

Environment Variables:

export EVA_API_URL="https://eva.example.com"
export EVA_API_TOKEN="your-api-token"

# Optional
export EVA_DEBUG="true"      # Enable API request logging
export MCP_DEBUG="true"      # Enable MCP server debug logging
export EVA_TIMEOUT="60s"     # Request timeout (default: 30s)
Usage with Claude Desktop

Add to your Claude Desktop configuration (~/.config/claude/claude_desktop_config.json):

{
  "mcpServers": {
    "evateam": {
      "command": "evateamclient-mcp",
      "args": ["--api-url", "https://eva.example.com", "--token", "your-api-token"]
    }
  }
}

Or using environment variables:

{
  "mcpServers": {
    "evateam": {
      "command": "evateamclient-mcp",
      "env": {
        "EVA_API_URL": "https://eva.example.com",
        "EVA_API_TOKEN": "your-api-token"
      }
    }
  }
}
Usage with Claude Code CLI

Option 1: Add via CLI (recommended)

# Add to user scope (available in all projects)
claude mcp add --transport stdio evateam --scope user \
  -e EVA_API_URL=https://eva.example.com \
  -e EVA_API_TOKEN=your-api-token \
  -- evateamclient-mcp

# Or add to project scope (shared via .mcp.json)
claude mcp add --transport stdio evateam --scope project \
  -e EVA_API_URL=https://eva.example.com \
  -e EVA_API_TOKEN=your-api-token \
  -- evateamclient-mcp

Option 2: Project config file (.mcp.json in project root)

Create .mcp.json in your project directory:

{
  "mcpServers": {
    "evateam": {
      "type": "stdio",
      "command": "evateamclient-mcp",
      "env": {
        "EVA_API_URL": "https://eva.example.com",
        "EVA_API_TOKEN": "your-api-token"
      }
    }
  }
}

Note: Project scope servers require user approval on first use. Claude Code will prompt for confirmation.

Option 3: Global user config (~/.claude.json)

Add to the mcpServers section in your ~/.claude.json:

{
  "projects": {
    "/your/project/path": {
      "mcpServers": {
        "evateam": {
          "type": "stdio",
          "command": "evateamclient-mcp",
          "env": {
            "EVA_API_URL": "https://eva.example.com",
            "EVA_API_TOKEN": "your-api-token"
          }
        }
      }
    }
  }
}

Verify connection:

claude mcp list
# Should show: evateam: evateamclient-mcp  - ✓ Connected
HTTP transport (Streamable HTTP)

Besides the default stdio transport, evateamclient-mcp can run as a Remote MCP server over HTTP using the Streamable HTTP protocol from MCP spec 2025-03-26+. This enables integration with clients that connect to MCP servers via URL — most notably Claude.ai web (cowork), which exposes only the "Add custom connector → Remote MCP server URL" form and cannot launch a local CLI.

HTTP-mode flags:

Flag Environment Default Description
--transport=http MCP_TRANSPORT=http stdio Switch from stdio to Streamable HTTP
--http-addr MCP_HTTP_ADDR 127.0.0.1:8080 Listen address (host:port)
--http-path MCP_HTTP_PATH /mcp Base path for the MCP endpoint
--http-stateless MCP_HTTP_STATELESS false Stateless mode: no session tracking, default init params per request
--http-json-response MCP_HTTP_JSON_RESPONSE false Return application/json instead of text/event-stream for responses

Endpoints exposed:

  • POST/GET/DELETE <http-path> — MCP Streamable HTTP endpoint (Claude.ai connects here)
  • GET /healthz — liveness probe, returns ok

Start the server:

evateamclient-mcp \
  --transport=http \
  --http-addr=127.0.0.1:8080 \
  --http-path=/mcp \
  --api-url="https://eva.example.com" \
  --token="your-api-token"

Smoke test with curl:

# Health check
curl -i http://127.0.0.1:8080/healthz
# HTTP/1.1 200 OK
# ok

# MCP initialize
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-06-18" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-06-18",
        "capabilities":{},
        "clientInfo":{"name":"curl","version":"1"}
      }}'
# => SSE stream with JSON-RPC result; Mcp-Session-Id is returned in response headers.
Usage with Claude.ai (cowork) — Remote MCP custom connector

Claude.ai web (chat / cowork) accepts only Remote MCP servers reachable over the public internet from Anthropic's IP range 160.79.104.0/21. There is no UI to launch a local stdio binary — you must expose the HTTP server through a public tunnel.

For a corporate EVA Team API that is reachable only from your machine (VPN / corporate network), this means: run evateamclient-mcp locally in HTTP mode, tunnel localhost:8080 to a public URL, and point Claude.ai at the tunnel URL. EVA traffic stays on your machine; only MCP traffic from Claude crosses the tunnel.

1. Start the HTTP server locally (from the section above):

evateamclient-mcp --transport=http --http-addr=127.0.0.1:8080 \
  --api-url="https://eva.example.com" --token="your-api-token"

2. Expose via a public tunnel. Any tool that gives an https://… URL pointing at localhost:8080 works.

# Option A: ngrok
ngrok http 8080
# => Forwarding  https://<random>.ngrok.app -> http://localhost:8080

# Option B: cloudflared
cloudflared tunnel --url http://localhost:8080
# => https://<random>.trycloudflare.com

3. Register the connector in Claude.ai:

Open Claude.ai → SettingsConnectorsAdd custom connector (BETA), then fill in:

  • Name: EVA Team
  • Remote MCP server URL: https://<random>.ngrok.app/mcp
  • Advanced settings: leave OAuth Client ID and OAuth Client Secret empty (authless mode — see security note below)

Click Add. Claude will perform initialize and discover the tools (eva_task_list, eva_project_list, …). You can then ask Claude prompts like "List my open tasks in project MYPROJ".

4. Security: protect the public tunnel. Once a tunnel URL is reachable from the internet, anyone who learns it can call EVA through your API token. Claude.ai does not support static bearer tokens for custom connectors, but you can use a hard-to-guess path segment as a shared secret:

SECRET=$(openssl rand -hex 16)
evateamclient-mcp --transport=http --http-path="/mcp/$SECRET" \
  --api-url="https://eva.example.com" --token="your-api-token"

# Use https://<random>.ngrok.app/mcp/<SECRET> in Claude.ai's "Remote MCP server URL"

Built-in protections:

  • DNS rebinding protection is enabled by default for 127.0.0.1 / [::1] listeners.
  • Cross-Origin Protection is applied via net/http.CrossOriginProtection.

Limitations. Claude.ai custom connectors do not accept static Authorization: Bearer … tokens. For multi-tenant scenarios (each Claude user authenticates with their own EVA account) full OAuth 2.0 / DCR is required and is not yet implemented in this server. Single-user authless + secret path is the supported mode today.

Available Tools

The MCP server provides 67+ tools for EVA Team operations:

Resource Tools
Task eva_task_list, eva_task_get, eva_task_create, eva_task_update, eva_task_delete, eva_task_update_status, eva_task_archive, eva_task_count
Project eva_project_list, eva_project_get, eva_project_create, eva_project_update, eva_project_delete, eva_project_add_executor, eva_project_remove_executor, eva_project_count
List eva_list_list, eva_list_get, eva_list_create, eva_list_update, eva_list_close, eva_list_delete, eva_list_count
Sprint eva_sprint_list, eva_sprint_get
Release eva_release_list, eva_release_get
Document eva_document_list, eva_document_get, eva_document_create, eva_document_update, eva_document_delete, eva_document_count, eva_document_page_tree
Person eva_person_list, eva_person_get, eva_person_count
TimeLog eva_timelog_list, eva_timelog_get, eva_timelog_create, eva_timelog_update, eva_timelog_delete, eva_timelog_count
Comment eva_comment_list, eva_comment_get, eva_comment_create, eva_comment_update, eva_comment_delete, eva_comment_count
Epic eva_epic_list, eva_epic_get, eva_epic_count
TaskLink eva_tasklink_list, eva_tasklink_get, eva_tasklink_create, eva_tasklink_delete, eva_tasklink_count
StatusHistory eva_statushistory_list, eva_statushistory_get, eva_statushistory_count
Stats eva_stats_project, eva_stats_sprint, eva_stats_timespent, eva_stats_sprint_executors_kpi
LogicType eva_logic_type_list, eva_logic_type_get
Tag eva_tag_list
Example Prompts

Once configured, you can ask Claude:

  • "Show me all open tasks in project MYPROJ"
  • "Create a new epic in project MYPROJ with name 'Q3 Auth Refactor'"
  • "Create a story under that epic tagged Frontend"
  • "Get statistics for sprint SPR-001543"
  • "List all comments on task MYPROJ-123"
  • "Log 2 hours on task MYPROJ-456"
  • "Show document page tree for DOC-029978"
  • "What logic types are available for tasks in this project?"
  • "List all tags in the system"

Support

License

BSD 3-Clause License - see LICENSE file for details

Contributing

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/amazing)
  3. Commit changes (git commit -am 'Add amazing feature')
  4. Push to branch (git push origin feature/amazing)
  5. Open Pull Request

Documentation

Index

Constants

View Source
const (
	CommentFieldID           = "id"
	CommentFieldClassName    = "class_name"
	CommentFieldText         = "text"
	CommentFieldLogLevel     = "log_level"
	CommentFieldParentID     = "parent_id" // parent task
	CommentFieldAuthorID     = "cmf_author_id"
	CommentFieldCmfCreatedAt = "cmf_created_at"
	CommentFieldCmfOwnerID   = "cmf_owner_id"
)

Comment field constants for type-safe queries

View Source
const (
	// Core fields
	DocumentFieldID              = "id"
	DocumentFieldClassName       = "class_name"
	DocumentFieldCode            = "code"
	DocumentFieldName            = "name"
	DocumentFieldText            = "text"
	DocumentFieldProjectID       = "project_id"
	DocumentFieldParentID        = "parent_id"
	DocumentFieldCacheStatusType = "cache_status_type"

	// System
	DocumentFieldCmfCreatedAt  = "cmf_created_at"
	DocumentFieldCmfModifiedAt = "cmf_modified_at"
	DocumentFieldCmfOwnerID    = "cmf_owner_id"
	DocumentFieldCmfDeleted    = "cmf_deleted"
)

Document field constants for type-safe queries

View Source
const (
	EntityProject       = "CmfProject"
	EntityTask          = "CmfTask"
	EntityDocument      = "CmfDocument"
	EntityList          = "CmfList" // Sprints, Releases
	EntityPerson        = "CmfPerson"
	EntityTimeLog       = "CmfTimeTrackerHistory"
	EntityComment       = "CmfComment"
	EntityRelation      = "CmfRelationOption" // Task links
	EntityFile          = "CmfRFile"
	EntityAudit         = "CmfAudit"
	EntityStatusHistory = "CmfStatusHistory"
	EntityLogicType     = "CmfLogicType"
	EntityTag           = "CmfTag"
)
View Source
const (
	// Core fields
	ListFieldID                = "id"
	ListFieldClassName         = "class_name"
	ListFieldCode              = "code"
	ListFieldName              = "name"
	ListFieldCacheStatusType   = "cache_status_type"
	ListFieldCacheMembersCount = "cache_members_count"
	ListFieldLimitDays         = "limit_days"

	// Relations
	ListFieldParent     = "parent" // nested project object
	ListFieldParentID   = "parent_id"
	ListFieldProjectID  = "project_id"
	ListFieldWorkflowID = "workflow_id"

	// Date fields
	ListFieldPlanStartDate = "plan_start_date"
	ListFieldPlanEndDate   = "plan_end_date"

	// Content
	ListFieldGoal = "goal"
	ListFieldText = "text"

	// System
	ListFieldSystem        = "system"
	ListFieldSlOwnerLock   = "sl_owner_lock"
	ListFieldCmfOwnerID    = "cmf_owner_id"
	ListFieldCmfCreatedAt  = "cmf_created_at"
	ListFieldCmfModifiedAt = "cmf_modified_at"

	// Code prefixes for list types
	ListCodePrefixSprint  = "SPR-"
	ListCodePrefixRelease = "REL-"
)

List field constants for type-safe queries

View Source
const (
	LogicTypeFieldID           = "id"
	LogicTypeFieldClassName    = "class_name"
	LogicTypeFieldName         = "name"
	LogicTypeFieldCode         = "code"
	LogicTypeFieldCmfModelName = "cmf_model_name"
	LogicTypeFieldParentID     = "parent_id"
	LogicTypeFieldProjectID    = "project_id"
)

Logic type field constants for type-safe queries

View Source
const (
	LogicTypeCodeEpic  = "task.epic:default"
	LogicTypeCodeStory = "task.userstory:story"
	LogicTypeCodeTask  = "task.agile:task"
	LogicTypeCodeBug   = "task.bug:default"
)

Well-known logic type codes for tasks. Actual codes are install-specific and may differ between deployments. The values below match the common defaults. Note: LogicTypeEpic ("task.epic") in epic.go is the legacy short form kept for backward compatibility with older deployments.

View Source
const (
	PersonFieldID    = "id"
	PersonFieldName  = "name"
	PersonFieldCode  = "code"
	PersonFieldLogin = "login"

	PersonFieldEmail          = "email"
	PersonFieldEmail2         = "email_2"
	PersonFieldPhone          = "phone"
	PersonFieldPhoneInternal  = "phone_internal"
	PersonFieldPhoneMobile    = "phone_mobile"
	PersonFieldPhone2         = "phone_2"
	PersonFieldPhoneAssistant = "phone_assistant"
	PersonFieldSkype          = "skype"
	PersonFieldTelegram       = "telegram"
	PersonFieldWhatsApp       = "whatsapp"
	PersonFieldSlack          = "slack"
	PersonFieldZoom           = "zoom"
	PersonFieldLinkedin       = "linkedin"
	PersonFieldFacebook       = "facebook"
	PersonFieldInstagram      = "instagram"
	PersonFieldVK             = "vk"
	PersonFieldOK             = "ok"

	PersonFieldFirstName    = "first_name"
	PersonFieldLastName     = "last_name"
	PersonFieldSecondName   = "second_name"
	PersonFieldWorkPosition = "work_position"
	PersonFieldBirthday     = "birthday"

	PersonFieldOnVacation     = "on_vacation"
	PersonFieldDoesNotWork    = "does_not_work"
	PersonFieldAvatarFilename = "avatar_filename"

	PersonFieldClientJobName    = "client_job_name"
	PersonFieldClientDepartment = "client_department"
	PersonFieldClientDivision   = "client_division"
	PersonFieldClientOffice     = "client_office"

	PersonFieldProjectID    = "project_id"
	PersonFieldParentID     = "parent_id"
	PersonFieldCmfOwnerID   = "cmf_owner_id"
	PersonFieldCreatedAt    = "cmf_created_at"
	PersonFieldModifiedAt   = "cmf_modified_at"
	PersonFieldDeletedLogin = "deleted_login"
	PersonFieldOldLogin     = "old_login"
	PersonFieldExtID        = "ext_id"
	PersonFieldOrderNo      = "orderno"
)

Person field constants for type-safe queries

View Source
const (
	ProjectFieldID                     = "id"
	ProjectFieldClassName              = "class_name"
	ProjectFieldCode                   = "code"
	ProjectFieldName                   = "name"
	ProjectFieldText                   = "text"
	ProjectFieldCMFLockedAt            = "cmf_locked_at"
	ProjectFieldCMFCreatedAt           = "cmf_created_at"
	ProjectFieldCMFModifiedAt          = "cmf_modified_at"
	ProjectFieldCMFViewedAt            = "cmf_viewed_at"
	ProjectFieldCMFDeleted             = "cmf_deleted"
	ProjectFieldCMFVersion             = "cmf_version"
	ProjectFieldCacheStatusType        = "cache_status_type"
	ProjectFieldWorkflowType           = "workflow_type"
	ProjectFieldWorkflowID             = "workflow_id"
	ProjectFieldParentID               = "parent_id"
	ProjectFieldCmfOwnerID             = "cmf_owner_id"
	ProjectFieldSystem                 = "system"
	ProjectFieldImportOriginal         = "import_original"
	ProjectFieldSlOwnerLock            = "sl_owner_lock"
	ProjectFieldPermParentOwnerID      = "perm_parent_owner_id"
	ProjectFieldPermInheritACLID       = "perm_inherit_acl_id"
	ProjectFieldPermEffectiveACLID     = "perm_effective_acl_id"
	ProjectFieldPermSecurityLevelCache = "perm_security_level_allowed_ids_cache"
	ProjectFieldIsTemplate             = "is_template"
	ProjectFieldExecutors              = "executors"
	ProjectFieldAdmins                 = "cmfprojectadmins"
	ProjectFieldSpectators             = "spectators"
	ProjectFieldOwnerAssistants        = "cmf_owner_assistants"
)

Project field constants for type-safe queries

View Source
const (
	// Core fields
	StatusHistoryFieldID        = "id"
	StatusHistoryFieldClassName = "class_name"
	StatusHistoryFieldCode      = "code"
	StatusHistoryFieldName      = "name"

	// Relations
	StatusHistoryFieldParentID    = "parent_id"     // entity that changed status
	StatusHistoryFieldProjectID   = "project_id"    // project context
	StatusHistoryFieldOldStatus   = "old_status"    // previous status value
	StatusHistoryFieldNewStatus   = "new_status"    // new status value
	StatusHistoryFieldOldStatusID = "old_status_id" // previous status ID
	StatusHistoryFieldNewStatusID = "new_status_id" // new status ID

	// System
	StatusHistoryFieldCmfOwnerID    = "cmf_owner_id"
	StatusHistoryFieldCmfCreatedAt  = "cmf_created_at"
	StatusHistoryFieldCmfModifiedAt = "cmf_modified_at"
)

StatusHistory field constants for type-safe queries

View Source
const (
	TagFieldID        = "id"
	TagFieldClassName = "class_name"
	TagFieldName      = "name"
	TagFieldCode      = "code"
	TagFieldAlias     = "alias"
	TagFieldParentID  = "parent_id"
	TagFieldProjectID = "project_id"
)

Tag field constants for type-safe queries

View Source
const (
	// Core fields
	TaskFieldID              = "id"
	TaskFieldClassName       = "class_name"
	TaskFieldCode            = "code"
	TaskFieldName            = "name"
	TaskFieldText            = "text"
	TaskFieldProjectID       = "project_id"
	TaskFieldParentID        = "parent_id"
	TaskFieldParentTask      = "parent_task"
	TaskFieldParentTaskID    = "parent_task_id"
	TaskFieldCacheStatusType = "cache_status_type"
	TaskFieldPriority        = "priority"
	TaskFieldDeadline        = "deadline"
	TaskFieldMark            = "mark"
	TaskFieldAlarmDate       = "alarm_date"

	// Story Points
	TaskFieldAgileStoryPoints = "agile_story_points"

	// Relations (single)
	TaskFieldResponsible   = "responsible"
	TaskFieldResponsibleID = "responsible_id"
	TaskFieldWaitingFor    = "waiting_for"
	TaskFieldEpic          = "epic"
	TaskFieldEpicID        = "epic_id"
	TaskFieldLogicType     = "logic_type"
	TaskFieldLogicTypeID   = "logic_type_id"
	TaskFieldCmfOwnerID    = "cmf_owner_id"
	TaskFieldWorkflowID    = "workflow_id"
	TaskFieldStatusID      = "status_id"
	TaskFieldStatus        = "status"

	// Relations (arrays)
	TaskFieldLists       = "lists" // sprints
	TaskFieldFixVersions = "fix_versions"
	TaskFieldTags        = "tags"
	TaskFieldExecutors   = "executors"
	TaskFieldSpectators  = "spectators"
	TaskFieldComponents  = "components"

	// Status tracking
	TaskFieldStatusModifiedAt      = "status_modified_at"
	TaskFieldStatusInProgressStart = "status_in_progress_start"
	TaskFieldStatusInProgressEnd   = "status_in_progress_end"
	TaskFieldStatusReviewAt        = "status_review_at"
	TaskFieldStatusClosedAt        = "status_closed_at"

	// Planning dates
	TaskFieldPlanStartDate  = "plan_start_date"
	TaskFieldPlanEndDate    = "plan_end_date"
	TaskFieldPeriodInterval = "period_interval"
	TaskFieldPeriodNextDate = "period_next_date"

	// Flags
	TaskFieldApproved  = "approved"
	TaskFieldIsPublic  = "is_public"
	TaskFieldNoControl = "no_control"
	TaskFieldIsFlagged = "is_flagged"

	// System
	TaskFieldCmfCreatedAt         = "cmf_created_at"
	TaskFieldCmfModifiedAt        = "cmf_modified_at"
	TaskFieldCmfViewedAt          = "cmf_viewed_at"
	TaskFieldCmfDeleted           = "cmf_deleted"
	TaskFieldCmfVersion           = "cmf_version"
	TaskFieldCmfLockedAt          = "cmf_locked_at"
	TaskFieldCacheChildTasksCount = "cache_child_tasks_count"
	TaskFieldExtID                = "ext_id"
	TaskFieldArchiveDate          = "archiveddate"
	TaskFieldResultText           = "result_text"
)

Task field constants for type-safe queries

View Source
const (
	// Core fields
	TaskLinkFieldID        = "id"
	TaskLinkFieldClassName = "class_name"
	TaskLinkFieldCode      = "code"
	TaskLinkFieldName      = "name"
	TaskLinkFieldInLink    = "in_link"  // filter field: incoming links to task
	TaskLinkFieldOutLink   = "out_link" // filter field: outgoing links from task

	// System
	TaskLinkFieldCmfCreatedAt  = "cmf_created_at"
	TaskLinkFieldCmfModifiedAt = "cmf_modified_at"
	TaskLinkFieldCmfOwnerID    = "cmf_owner_id"
)

TaskLink field constants for type-safe queries

View Source
const (
	// Core fields
	TimeLogFieldID        = "id"
	TimeLogFieldClassName = "class_name"
	TimeLogFieldCode      = "code"
	TimeLogFieldName      = "name"
	TimeLogFieldTimeSpent = "time_spent"

	// Relations
	TimeLogFieldParent   = "parent"    // nested task object
	TimeLogFieldParentID = "parent_id" // task ID (CmfTask:uuid)

	// System
	TimeLogFieldProjectID     = "project_id"
	TimeLogFieldCmfOwnerID    = "cmf_owner_id" // person who logged time
	TimeLogFieldCmfCreatedAt  = "cmf_created_at"
	TimeLogFieldCmfModifiedAt = "cmf_modified_at"
)

TimeLog field constants for type-safe queries

View Source
const (
	// LogicTypeEpic is the logic_type.code for epics
	LogicTypeEpic = "task.epic"
)

Variables

View Source
var (
	// DefaultCommentFields - standard projection for single comment queries
	DefaultCommentFields = []string{
		CommentFieldID,
		CommentFieldClassName,
		CommentFieldText,
		CommentFieldAuthorID,
		CommentFieldParentID,
		CommentFieldCmfCreatedAt,
		CommentFieldLogLevel,
	}

	// DefaultCommentListFields - optimized for LIST queries (lighter payload)
	DefaultCommentListFields = []string{
		CommentFieldID,
		CommentFieldText,
		CommentFieldAuthorID,
		CommentFieldCmfCreatedAt,
	}
)
View Source
var (
	// DefaultDocumentFields - standard projection for single document queries
	DefaultDocumentFields = []string{
		DocumentFieldID,
		DocumentFieldClassName,
		DocumentFieldCode,
		DocumentFieldName,
		DocumentFieldText,
		DocumentFieldProjectID,
		DocumentFieldCacheStatusType,
		DocumentFieldCmfCreatedAt,
		DocumentFieldCmfModifiedAt,
	}

	// DefaultDocumentListFields - optimized for LIST queries
	DefaultDocumentListFields = []string{
		DocumentFieldID,
		DocumentFieldCode,
		DocumentFieldName,
		DocumentFieldProjectID,
		DocumentFieldCacheStatusType,
		DocumentFieldCmfCreatedAt,
	}
)
View Source
var (
	// DefaultEpicFields - standard projection for epic queries
	DefaultEpicFields = []string{
		TaskFieldID,
		TaskFieldClassName,
		TaskFieldCode,
		TaskFieldName,
		TaskFieldText,
		TaskFieldProjectID,
		TaskFieldCacheStatusType,
		TaskFieldLogicType,
	}

	// DefaultEpicListFields - optimized for LIST queries
	DefaultEpicListFields = []string{
		TaskFieldID,
		TaskFieldCode,
		TaskFieldName,
		TaskFieldProjectID,
		TaskFieldCacheStatusType,
	}
)
View Source
var (
	ErrOptionIsRequired    = errors.New("option is required")
	ErrBodyIsRequired      = errors.New("body is required")
	ErrRPCMethodIsRequired = errors.New("RPCRequest.Method is required")
)
View Source
var (
	// DefaultPersonFields - standard projection for single person queries
	DefaultPersonFields = []string{
		PersonFieldID,
		PersonFieldName,
		PersonFieldCode,
		PersonFieldLogin,
		PersonFieldEmail,
		PersonFieldOnVacation,
		PersonFieldDoesNotWork,
		PersonFieldCreatedAt,
	}

	// DefaultPersonListFields - optimized for LIST queries (lighter payload)
	DefaultPersonListFields = []string{
		PersonFieldID,
		PersonFieldName,
		PersonFieldCode,
		PersonFieldLogin,
		PersonFieldEmail,
		PersonFieldOnVacation,
		PersonFieldDoesNotWork,
	}
)
View Source
var (
	AllBasicFields                  = []string{"*"}
	AllBasicAndRelationFields       = []string{"**"}
	AllBasicAndRelationAndM2MFields = []string{"***"}
)
View Source
var (
	// DefaultStatusHistoryFields - standard projection for single status history queries
	DefaultStatusHistoryFields = []string{
		StatusHistoryFieldID,
		StatusHistoryFieldCode,
		StatusHistoryFieldParentID,
		StatusHistoryFieldOldStatus,
		StatusHistoryFieldNewStatus,
		StatusHistoryFieldCmfOwnerID,
		StatusHistoryFieldCmfCreatedAt,
	}

	// DefaultStatusHistoryListFields - optimized for LIST queries (lighter payload)
	DefaultStatusHistoryListFields = []string{
		StatusHistoryFieldID,
		StatusHistoryFieldCode,
		StatusHistoryFieldParentID,
		StatusHistoryFieldOldStatus,
		StatusHistoryFieldNewStatus,
		StatusHistoryFieldCmfCreatedAt,
	}
)
View Source
var (
	// DefaultTaskLinkFields - standard projection for task link queries
	DefaultTaskLinkFields = []string{
		TaskLinkFieldID,
		TaskLinkFieldClassName,
		TaskLinkFieldCode,
		TaskLinkFieldName,
		TaskLinkFieldCmfCreatedAt,
		TaskLinkFieldCmfOwnerID,
	}

	// DefaultTaskLinkListFields - optimized for LIST queries
	DefaultTaskLinkListFields = []string{
		TaskLinkFieldID,
		TaskLinkFieldCode,
		TaskLinkFieldName,
	}
)
View Source
var (
	// DefaultTimeLogFields - standard projection for single time log queries
	DefaultTimeLogFields = []string{
		TimeLogFieldID,
		TimeLogFieldCode,
		TimeLogFieldTimeSpent,
		TimeLogFieldParent,
		TimeLogFieldParentID,
		TimeLogFieldProjectID,
		TimeLogFieldCmfOwnerID,
		TimeLogFieldCmfCreatedAt,
	}

	// DefaultTimeLogListFields - optimized for LIST queries (lighter payload)
	DefaultTimeLogListFields = []string{
		TimeLogFieldID,
		TimeLogFieldCode,
		TimeLogFieldTimeSpent,
		TimeLogFieldParent,
		TimeLogFieldCmfCreatedAt,
	}
)

DefaultLogicTypeFields - standard projection for LogicType queries

DefaultTagFields - standard projection for Tag queries

Functions

func Between

func Between(col string, from, to any) sq.And

Between creates a range filter for EVA using Squirrel's And combinator Example: qb.Where(Between("cmf_created_at", "2024-01-01", "2024-12-31"))

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the EVA Team API client

func NewClient

func NewClient(cfg *Config, opts ...Option) (*Client, error)

func (*Client) Close

func (c *Client) Close() error

Close closes client

func (*Client) Comment

func (c *Client) Comment(
	ctx context.Context,
	commentID string,
	fields []string,
) (*models.Comment, *models.Meta, error)

Comment retrieves a single comment by ID Example:

comment, meta, err := client.Comment(ctx, "Comment:uuid", nil)

func (*Client) CommentCount

func (c *Client) CommentCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

CommentCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityComment).
  Where(sq.Eq{"task_id": "Task:PROJ-123"})
count, err := client.CommentCount(ctx, qb)

func (*Client) CommentCreate

func (c *Client) CommentCreate(
	ctx context.Context,
	taskID string,
	text string,
) (*models.Comment, error)

CommentCreate creates a new comment on a task Example:

comment, err := client.CommentCreate(ctx, "Task:PROJ-123", "This is a comment")

func (*Client) CommentDelete

func (c *Client) CommentDelete(
	ctx context.Context,
	commentID string,
) error

CommentDelete deletes a comment by ID Example:

err := client.CommentDelete(ctx, "Comment:uuid")

func (*Client) CommentQuery

func (c *Client) CommentQuery(ctx context.Context, qb *QueryBuilder) (*models.Comment, *models.Meta, error)

CommentQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "text", "cmf_author_id").
  From(evateamclient.EntityComment).
  Where(sq.Eq{"id": "Comment:uuid"})
comment, meta, err := client.CommentQuery(ctx, qb)

func (*Client) CommentUpdate

func (c *Client) CommentUpdate(
	ctx context.Context,
	commentID string,
	text string,
) (*models.Comment, error)

CommentUpdate updates an existing comment Example:

comment, err := client.CommentUpdate(ctx, "Comment:uuid", "Updated text")

func (*Client) Comments

func (c *Client) Comments(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.Comment, *models.Meta, error)

Comments retrieves comments with custom filters (backward compatible, deprecated) Recommended: use CommentsList with NewQueryBuilder() instead

func (*Client) CommentsList

func (c *Client) CommentsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.Comment, *models.Meta, error)

CommentsList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "text", "cmf_author_id", "cmf_created_at").
  From(evateamclient.EntityComment).
  Where(sq.Eq{"task_id": "Task:PROJ-123"}).
  OrderBy("-cmf_created_at").
  Offset(0).Limit(100)
comments, meta, err := client.CommentsList(ctx, qb)

func (*Client) Document

func (c *Client) Document(
	ctx context.Context,
	docCode string,
	fields []string,
) (*models.Document, *models.Meta, error)

Document retrieves a single document by code Example:

doc, meta, err := client.Document(ctx, "DOC-123", nil)

func (*Client) DocumentCount

func (c *Client) DocumentCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

DocumentCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityDocument).
  Where(sq.Eq{"project_id": "Project:uuid"})
count, err := client.DocumentCount(ctx, qb)

func (*Client) DocumentCreate

func (c *Client) DocumentCreate(
	ctx context.Context,
	params DocumentCreateParams,
) (*models.Document, error)

DocumentCreate creates a new document Example:

params := evateamclient.DocumentCreateParams{
  Name:      "New Document",
  ProjectID: "Project:uuid",
  Text:      "Document content",
}
doc, err := client.DocumentCreate(ctx, params)

func (*Client) DocumentDelete

func (c *Client) DocumentDelete(
	ctx context.Context,
	docID string,
) error

DocumentDelete deletes a document by ID Example:

err := client.DocumentDelete(ctx, "CmfDocument:uuid")

func (*Client) DocumentPageTree added in v1.3.0

func (c *Client) DocumentPageTree(
	ctx context.Context,
	nodeID string,
) ([]models.Document, error)

DocumentPageTree retrieves the document page tree hierarchy starting from the given node. Returns a flat list of documents with parent_id and tree_node_is_branch fields for building a tree structure. Example:

docs, err := client.DocumentPageTree(ctx, "CmfDocument:uuid")

func (*Client) DocumentQuery

func (c *Client) DocumentQuery(ctx context.Context, qb *QueryBuilder) (*models.Document, *models.Meta, error)

DocumentQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name", "text").
  From(evateamclient.EntityDocument).
  Where(sq.Eq{"code": "DOC-123"})
doc, meta, err := client.DocumentQuery(ctx, qb)

func (*Client) DocumentUpdate

func (c *Client) DocumentUpdate(
	ctx context.Context,
	docID string,
	updates map[string]any,
) (*models.Document, error)

DocumentUpdate updates an existing document Example:

updates := map[string]any{
  "name": "Updated Document Name",
  "text": "Updated content",
}
doc, err := client.DocumentUpdate(ctx, "CmfDocument:uuid", updates)

func (*Client) Documents

func (c *Client) Documents(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.Document, *models.Meta, error)

Documents retrieves documents with custom filters (backward compatible, deprecated) Recommended: use DocumentsList with NewQueryBuilder() instead

func (*Client) DocumentsList

func (c *Client) DocumentsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.Document, *models.Meta, error)

DocumentsList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name").
  From(evateamclient.EntityDocument).
  Where(sq.Eq{"project_id": "Project:uuid"}).
  OrderBy("-cmf_created_at").
  Offset(0).Limit(100)
docs, meta, err := client.DocumentsList(ctx, qb)

func (*Client) Epic

func (c *Client) Epic(
	ctx context.Context,
	epicCode string,
	fields []string,
) (*models.Task, *models.Meta, error)

Epic retrieves a single epic by code Note: Epics are CmfTask with logic_type.code = "task.epic" Example:

epic, meta, err := client.Epic(ctx, "UDMP-123", nil)

func (*Client) EpicByID

func (c *Client) EpicByID(
	ctx context.Context,
	epicID string,
	fields []string,
) (*models.Task, *models.Meta, error)

EpicByID retrieves a single epic by ID Example:

epic, meta, err := client.EpicByID(ctx, "CmfTask:uuid", nil)

func (*Client) EpicTasks

func (c *Client) EpicTasks(
	ctx context.Context,
	epicID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

EpicTasks retrieves ALL tasks in epic by epic ID Example:

tasks, meta, err := client.EpicTasks(ctx, "CmfTask:uuid", nil)

func (*Client) Epics

func (c *Client) Epics(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.TaskBrowse, *models.Meta, error)

Epics retrieves epics with custom kwargs Example:

epics, meta, err := client.Epics(ctx, map[string]any{
  "filter": [][]any{
    {"project_id", "==", "CmfProject:uuid"},
    {"logic_type.code", "==", "task.epic"},
  },
})

func (*Client) List

func (c *Client) List(
	ctx context.Context,
	listCode string,
	fields []string,
) (*models.List, *models.Meta, error)

List retrieves a single list (sprint/release) by code Example:

list, meta, err := client.List(ctx, "SPR-001543", nil)
list, meta, err := client.List(ctx, "REL-001641", nil)

func (*Client) ListClose

func (c *Client) ListClose(
	ctx context.Context,
	listID string,
) (*models.List, error)

ListClose closes a list (sprint/release) Example:

list, err := client.ListClose(ctx, "CmfList:uuid")

func (*Client) ListCount

func (c *Client) ListCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

ListCount counts lists using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityList).
  Where(sq.Eq{"project_id": "CmfProject:uuid"})
count, err := client.ListCount(ctx, qb)

func (*Client) ListCreate

func (c *Client) ListCreate(
	ctx context.Context,
	params *ListCreateParams,
) (*models.List, error)

ListCreate creates a new list (sprint/release) Example:

params := evateamclient.ListCreateParams{
  Name:     "Sprint 1",
  ParentID: "CmfProject:uuid",
}
list, err := client.ListCreate(ctx, params)

func (*Client) ListDelete

func (c *Client) ListDelete(
	ctx context.Context,
	listID string,
) error

ListDelete deletes a list by ID Example:

err := client.ListDelete(ctx, "CmfList:uuid")

func (*Client) ListQuery

func (c *Client) ListQuery(ctx context.Context, qb *QueryBuilder) (*models.List, *models.Meta, error)

ListQuery executes query using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "name", "code", "cache_status_type").
  From(evateamclient.EntityList).
  Where(sq.Eq{"code": "SPR-001543"})
list, meta, err := client.ListQuery(ctx, qb)

func (*Client) ListTasksCount

func (c *Client) ListTasksCount(ctx context.Context, listCode string) (int64, *models.Meta, error)

ListTasksCount returns total tasks in list (sprint/release) by list code.

func (*Client) ListUpdate

func (c *Client) ListUpdate(
	ctx context.Context,
	listID string,
	updates map[string]any,
) (*models.List, error)

ListUpdate updates an existing list Example:

updates := map[string]any{
  "name": "Updated Name",
  "goal": "Complete feature X",
}
list, err := client.ListUpdate(ctx, "CmfList:uuid", updates)

func (*Client) Lists

func (c *Client) Lists(ctx context.Context, kwargs map[string]any) ([]models.List, *models.Meta, error)

Lists retrieves lists with custom kwargs Example:

lists, meta, err := client.Lists(ctx, map[string]any{
  "filter": []any{"project_id", "==", "CmfProject:uuid"},
})

func (*Client) ListsList

func (c *Client) ListsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.List, *models.Meta, error)

ListsList retrieves lists using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name", "cache_status_type").
  From(evateamclient.EntityList).
  Where(sq.Eq{"project_id": "CmfProject:uuid"}).
  Where(sq.Eq{"cache_status_type": evateamclient.StatusTypeOpen}).
  Offset(0).Limit(50)
lists, meta, err := client.ListsList(ctx, qb)

func (*Client) LogicTypeByCode added in v1.4.0

func (c *Client) LogicTypeByCode(
	ctx context.Context,
	code string,
) (*models.LogicType, error)

LogicTypeByCode retrieves a single LogicType by its code (e.g. "task.epic", "task.story"). Returns an error if no logic type with that code is found on the server. Example:

lt, err := client.LogicTypeByCode(ctx, evateamclient.LogicTypeEpic)
// lt.ID -> "CmfLogicType:..."

func (*Client) LogicTypeList added in v1.4.0

func (c *Client) LogicTypeList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.LogicType, *models.Meta, error)

LogicTypeList retrieves logic types using a QueryBuilder. Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name").
  From(evateamclient.EntityLogicType).
  Where(sq.Eq{"code": "task.epic"})
items, meta, err := client.LogicTypeList(ctx, qb)

func (*Client) OpenProjectLists

func (c *Client) OpenProjectLists(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

OpenProjectLists retrieves all open lists for project Example:

lists, meta, err := client.OpenProjectLists(ctx, "CmfProject:uuid", nil)

func (*Client) OpenProjectReleases

func (c *Client) OpenProjectReleases(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

OpenProjectReleases retrieves open releases for project Example:

releases, meta, err := client.OpenProjectReleases(ctx, "CmfProject:uuid", nil)

func (*Client) OpenProjectSprints

func (c *Client) OpenProjectSprints(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

OpenProjectSprints retrieves open sprints for project Example:

sprints, meta, err := client.OpenProjectSprints(ctx, "CmfProject:uuid", nil)

func (*Client) Person

func (c *Client) Person(
	ctx context.Context,
	userID string,
	fields []string,
) (*models.Person, *models.Meta, error)

Person retrieves a single person by ID (backward compatible) Example:

person, meta, err := client.Person(ctx, "Person:uuid-here", nil)

func (*Client) PersonCount

func (c *Client) PersonCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

PersonCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityPerson).
  Where(sq.Eq{"does_not_work": false})
count, err := client.PersonCount(ctx, qb)

func (*Client) PersonProjectTasks

func (c *Client) PersonProjectTasks(
	ctx context.Context,
	projectID,
	userID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

PersonProjectTasks retrieves user's tasks as responsible in specific project Example:

tasks, meta, err := client.PersonProjectTasks(ctx, "Project:uuid", "Person:uuid", nil)

func (*Client) PersonQuery

func (c *Client) PersonQuery(ctx context.Context, qb *QueryBuilder) (*models.Person, *models.Meta, error)

PersonQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "name", "email").
  From(evateamclient.EntityPerson).
  Where(sq.Eq{"login": "john.doe"})
person, meta, err := client.PersonQuery(ctx, qb)

func (*Client) PersonTasks

func (c *Client) PersonTasks(
	ctx context.Context,
	userID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

PersonTasks retrieves ALL tasks where user is responsible Note: For OR logic (responsible OR executor), use PersonTasksAll Example:

tasks, meta, err := client.PersonTasks(ctx, "Person:uuid", nil)

func (*Client) PersonTasksAsExecutor

func (c *Client) PersonTasksAsExecutor(
	ctx context.Context,
	userID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

PersonTasksAsExecutor retrieves tasks where user is in executors list Example:

tasks, meta, err := client.PersonTasksAsExecutor(ctx, "Person:uuid", nil)

func (*Client) Persons

func (c *Client) Persons(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.Person, *models.Meta, error)

Persons retrieves users with custom filters (backward compatible, deprecated) Recommended: use PersonsList with NewQueryBuilder() instead

func (*Client) PersonsList

func (c *Client) PersonsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.Person, *models.Meta, error)

PersonsList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "name", "email").
  From(evateamclient.EntityPerson).
  Where(sq.Eq{"on_vacation": false}).
  Where(sq.Eq{"does_not_work": false}).
  OrderBy("name").
  Offset(0).Limit(100)
persons, meta, err := client.PersonsList(ctx, qb)

func (*Client) Project

func (c *Client) Project(
	ctx context.Context,
	code string,
	fields []string,
) (*models.Project, *models.Meta, error)

Project retrieves a single project by code (backward compatible) Example:

project, meta, err := client.Project(ctx, "PROJ-123", nil)

func (*Client) ProjectAddExecutor

func (c *Client) ProjectAddExecutor(
	ctx context.Context,
	projectID, personID string,
) error

ProjectAddExecutor adds an executor to project Example:

err := client.ProjectAddExecutor(ctx, "Project:uuid", "Person:uuid")

func (*Client) ProjectCount

func (c *Client) ProjectCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

ProjectCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityProject).
  Where(sq.Eq{"system": false})
count, err := client.ProjectCount(ctx, qb)

func (*Client) ProjectCreate

func (c *Client) ProjectCreate(
	ctx context.Context,
	params *ProjectCreateParams,
) (*models.Project, error)

ProjectCreate creates a new project Example:

params := evateamclient.ProjectCreateParams{
  Code: "NEWPROJ",
  Name: "New Project",
}
project, err := client.ProjectCreate(ctx, params)

func (*Client) ProjectDelete

func (c *Client) ProjectDelete(
	ctx context.Context,
	projectID string,
) error

ProjectDelete deletes a project by ID Example:

err := client.ProjectDelete(ctx, "Project:uuid")

func (*Client) ProjectDocuments

func (c *Client) ProjectDocuments(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.Document, *models.Meta, error)

ProjectDocuments retrieves ALL documents in project Example:

docs, meta, err := client.ProjectDocuments(ctx, "Project:uuid", nil)

func (*Client) ProjectEpics

func (c *Client) ProjectEpics(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

ProjectEpics retrieves ALL epics in project Example:

epics, meta, err := client.ProjectEpics(ctx, "CmfProject:uuid", nil)

func (*Client) ProjectLists

func (c *Client) ProjectLists(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

ProjectLists retrieves ALL lists (sprints + releases) for project Example:

lists, meta, err := client.ProjectLists(ctx, "CmfProject:uuid", nil)

func (*Client) ProjectQuery

func (c *Client) ProjectQuery(
	ctx context.Context,
	qb *QueryBuilder,
) (*models.Project, *models.Meta, error)

ProjectQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "name", "executors").
  From(evateamclient.EntityProject).
  Where(sq.Eq{"code": "PROJ-123"})
project, meta, err := client.ProjectQuery(ctx, qb)

func (*Client) ProjectReleases

func (c *Client) ProjectReleases(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

ProjectReleases retrieves all releases for project (code like "REL-%") Example:

releases, meta, err := client.ProjectReleases(ctx, "CmfProject:uuid", nil)

func (*Client) ProjectRemoveExecutor

func (c *Client) ProjectRemoveExecutor(
	ctx context.Context,
	projectID, personID string,
) error

ProjectRemoveExecutor removes an executor from project Example:

err := client.ProjectRemoveExecutor(ctx, "Project:uuid", "Person:uuid")

func (*Client) ProjectSprints

func (c *Client) ProjectSprints(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.List, *models.Meta, error)

ProjectSprints retrieves all sprints for project (code like "SPR-%") Example:

sprints, meta, err := client.ProjectSprints(ctx, "CmfProject:uuid", nil)

func (*Client) ProjectStats

func (c *Client) ProjectStats(ctx context.Context, projectID string) (*models.ProjectStats, *models.Meta, error)

ProjectStats retrieves project statistics.

func (*Client) ProjectStatusHistory

func (c *Client) ProjectStatusHistory(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.StatusHistory, *models.Meta, error)

ProjectStatusHistory retrieves ALL status changes for project entities Example:

histories, meta, err := client.ProjectStatusHistory(ctx, "CmfProject:uuid", nil)

func (*Client) ProjectTasks

func (c *Client) ProjectTasks(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

ProjectTasks retrieves ALL tasks for project (backward compatible) Example:

tasks, meta, err := client.ProjectTasks(ctx, "Project:uuid", nil)

func (*Client) ProjectTasksCount

func (c *Client) ProjectTasksCount(ctx context.Context, projectID string) (int64, *models.Meta, error)

ProjectTasksCount returns total tasks in project.

func (*Client) ProjectTimeLogs

func (c *Client) ProjectTimeLogs(
	ctx context.Context,
	projectID string,
	fields []string,
) ([]models.TimeLog, *models.Meta, error)

ProjectTimeLogs retrieves ALL time entries for project tasks (backward compatible) Note: This uses dot notation for nested field filtering Example:

logs, meta, err := client.ProjectTimeLogs(ctx, "Project:uuid", nil)

func (*Client) ProjectUpdate

func (c *Client) ProjectUpdate(
	ctx context.Context,
	projectID string,
	updates map[string]any,
) (*models.Project, error)

ProjectUpdate updates an existing project Example:

updates := map[string]any{
  "name": "Updated Project Name",
}
project, err := client.ProjectUpdate(ctx, "Project:uuid", updates)

func (*Client) Projects

func (c *Client) Projects(
	ctx context.Context,
	fields []string,
	kwargs map[string]any,
) ([]models.Project, *models.Meta, error)

Projects retrieves a list of projects (backward compatible, deprecated) Recommended: use ProjectsSquirrel with NewEvaBuilder() instead

func (*Client) ProjectsList

func (c *Client) ProjectsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.Project, *models.Meta, error)

ProjectsList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name").
  From(evateamclient.EntityProject).
  Where(sq.Like{"name": "%Mobile%"}).
  Where(sq.Eq{"system": false}).
  OrderBy("-cmf_created_at").
  Offset(0).Limit(50)
projects, meta, err := client.ProjectsList(ctx, qb)

func (*Client) Releases

func (c *Client) Releases(ctx context.Context, kwargs map[string]any) ([]models.List, *models.Meta, error)

Releases retrieves releases with custom kwargs Example:

releases, meta, err := client.Releases(ctx, map[string]any{
  "filter": [][]any{
    {"project_id", "==", "CmfProject:uuid"},
    {"code", "LIKE", "REL-%"},
  },
})

func (*Client) SprintExecutorsKPI added in v1.2.1

func (c *Client) SprintExecutorsKPI(ctx context.Context, params *SprintExecutorsKPIParams) (*models.SprintExecutorsKPI, error)

SprintExecutorsKPI builds executor KPI for closed tasks in a sprint.

Scope rules: - Task belongs to sprint list (`task.lists` contains sprint code or list id) - Task closed in sprint date range (`status_closed_at` in [start_date, end_date]) - Tasks appeared during sprint are excluded:

  • if BaselineTaskIDs provided: only those IDs are counted
  • otherwise: task.cmf_created_at must be <= sprint.start_date

func (*Client) SprintStats

func (c *Client) SprintStats(ctx context.Context, sprintCode string) (*models.SprintStats, error)

SprintStats retrieves sprint statistics.

func (*Client) SprintTasks

func (c *Client) SprintTasks(
	ctx context.Context,
	sprintCode string,
	fields []string,
) ([]models.TaskBrowse, *models.Meta, error)

SprintTasks retrieves ALL tasks for sprint (backward compatible) Example:

tasks, meta, err := client.SprintTasks(ctx, "SPRINT-CODE", nil)

func (*Client) SprintTasksCount

func (c *Client) SprintTasksCount(ctx context.Context, sprintCode string) (int64, *models.Meta, error)

SprintTasksCount returns total tasks in sprint by list code.

func (*Client) Sprints

func (c *Client) Sprints(ctx context.Context, kwargs map[string]any) ([]models.List, *models.Meta, error)

Sprints retrieves sprints with custom kwargs Example:

sprints, meta, err := client.Sprints(ctx, map[string]any{
  "filter": [][]any{
    {"project_id", "==", "CmfProject:uuid"},
    {"code", "LIKE", "SPR-%"},
  },
})

func (*Client) StatusHistories

func (c *Client) StatusHistories(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.StatusHistory, *models.Meta, error)

StatusHistories retrieves status histories with custom filters (backward compatible) Recommended: use StatusHistoryList with NewQueryBuilder() instead

func (*Client) StatusHistory

func (c *Client) StatusHistory(
	ctx context.Context,
	statusHistoryID string,
	fields []string,
) (*models.StatusHistory, *models.Meta, error)

StatusHistory retrieves a single status history entry by ID Example:

history, meta, err := client.StatusHistory(ctx, "CmfStatusHistory:uuid", nil)

func (*Client) StatusHistoryCount

func (c *Client) StatusHistoryCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

StatusHistoryCount counts status history entries using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityStatusHistory).
  Where(sq.Eq{"parent_id": "CmfTask:uuid"})
count, err := client.StatusHistoryCount(ctx, qb)

func (*Client) StatusHistoryList

func (c *Client) StatusHistoryList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.StatusHistory, *models.Meta, error)

StatusHistoryList retrieves list using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "old_status", "new_status", "cmf_created_at").
  From(evateamclient.EntityStatusHistory).
  Where(sq.Eq{"parent_id": "CmfTask:uuid"}).
  OrderBy("-cmf_created_at").
  Offset(0).Limit(100)
histories, meta, err := client.StatusHistoryList(ctx, qb)

func (*Client) StatusHistoryQuery

func (c *Client) StatusHistoryQuery(ctx context.Context, qb *QueryBuilder) (*models.StatusHistory, *models.Meta, error)

StatusHistoryQuery executes query using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "old_status", "new_status", "cmf_created_at").
  From(evateamclient.EntityStatusHistory).
  Where(sq.Eq{"id": "CmfStatusHistory:uuid"})
history, meta, err := client.StatusHistoryQuery(ctx, qb)

func (*Client) TagList added in v1.4.0

func (c *Client) TagList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.Tag, *models.Meta, error)

TagList retrieves tags using a QueryBuilder. Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name", "alias").
  From(evateamclient.EntityTag).
  Where(sq.Eq{"project_id": "CmfProject:uuid"})
items, meta, err := client.TagList(ctx, qb)

func (*Client) Task

func (c *Client) Task(
	ctx context.Context,
	taskCode string,
	fields []string,
) (*models.Task, *models.Meta, error)

Task retrieves a single task by code (backward compatible) Example:

task, meta, err := client.Task(ctx, "PROJ-123", nil)

func (*Client) TaskArchive

func (c *Client) TaskArchive(
	ctx context.Context,
	taskID string,
) error

TaskArchive archives a task (soft delete) Example:

err := client.TaskArchive(ctx, "CmfTask:uuid")

func (*Client) TaskComments

func (c *Client) TaskComments(
	ctx context.Context,
	taskCode string,
	fields []string,
) ([]models.Comment, *models.Meta, error)

TaskComments retrieves ALL comments for task (backward compatible) Example:

comments, meta, err := client.TaskComments(ctx, "PROJ-123", nil)

func (*Client) TaskCommentsByID

func (c *Client) TaskCommentsByID(
	ctx context.Context,
	taskID string,
	fields []string,
) ([]models.Comment, *models.Meta, error)

TaskCommentsByID retrieves ALL comments for task by task ID Example:

comments, meta, err := client.TaskCommentsByID(ctx, "CmfTask:uuid", nil)

func (*Client) TaskCount

func (c *Client) TaskCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

TaskCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityTask).
  Where(sq.Eq{"project_id": "Project:uuid"}).
  Where(sq.Eq{"cache_status_type": evateamclient.StatusTypeOpen})
count, err := client.TaskCount(ctx, qb)

func (*Client) TaskCreate

func (c *Client) TaskCreate(
	ctx context.Context,
	params *TaskCreateParams,
) (*models.Task, error)

TaskCreate creates a new task Example:

params := evateamclient.TaskCreateParams{
  Name:      "New Task",
  ProjectID: "Project:uuid",
  Priority:  3,
}
task, err := client.TaskCreate(ctx, params)

func (*Client) TaskDelete

func (c *Client) TaskDelete(
	ctx context.Context,
	taskID string,
) error

TaskDelete deletes a task by ID Example:

err := client.TaskDelete(ctx, "CmfTask:uuid")
func (c *Client) TaskLink(
	ctx context.Context,
	linkID string,
	fields []string,
) (*models.TaskLink, *models.Meta, error)

TaskLink retrieves a single task link by ID Example:

link, meta, err := client.TaskLink(ctx, "CmfTaskLink:uuid", nil)

func (*Client) TaskLinkCount

func (c *Client) TaskLinkCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

TaskLinkCount counts task links using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityRelation).
  Where(sq.Eq{evateamclient.TaskLinkFieldOutLink: "CmfTask:uuid"})
count, err := client.TaskLinkCount(ctx, qb)

func (*Client) TaskLinkCreate

func (c *Client) TaskLinkCreate(
	ctx context.Context,
	sourceTaskID, targetTaskID, relationOptionID string,
) (*models.TaskLink, error)

TaskLinkCreate creates a new task link Example:

link, err := client.TaskLinkCreate(ctx, "CmfTask:uuid1", "CmfTask:uuid2", "RLO-000001")

func (*Client) TaskLinkDelete

func (c *Client) TaskLinkDelete(
	ctx context.Context,
	linkID string,
) error

TaskLinkDelete deletes a task link by ID Example:

err := client.TaskLinkDelete(ctx, "RLO-000001")

func (*Client) TaskLinkQuery

func (c *Client) TaskLinkQuery(ctx context.Context, qb *QueryBuilder) (*models.TaskLink, *models.Meta, error)

TaskLinkQuery executes query using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name").
  From(evateamclient.EntityRelation).
  Where(sq.Eq{"id": "CmfRelationOption:uuid"})
link, meta, err := client.TaskLinkQuery(ctx, qb)
func (c *Client) TaskLinks(
	ctx context.Context,
	taskID string,
	fields []string,
) ([]models.TaskLink, *models.Meta, error)

TaskLinks retrieves ALL task relationships (both directions) Makes two API calls (outgoing + incoming) and merges results Example:

links, meta, err := client.TaskLinks(ctx, "CmfTask:uuid", nil)

func (*Client) TaskLinksIncoming

func (c *Client) TaskLinksIncoming(
	ctx context.Context,
	taskID string,
	fields []string,
) ([]models.TaskLink, *models.Meta, error)

TaskLinksIncoming retrieves links where task is target (incoming) Example:

links, meta, err := client.TaskLinksIncoming(ctx, "CmfTask:uuid", nil)
func (c *Client) TaskLinksList(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.TaskLink, *models.Meta, error)

TaskLinksList retrieves task links with custom filters (backward compatible, deprecated) Recommended: use TaskLinksListQuery with NewQueryBuilder() instead

func (*Client) TaskLinksListQuery

func (c *Client) TaskLinksListQuery(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.TaskLink, *models.Meta, error)

TaskLinksListQuery retrieves list using QueryBuilder Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name").
  From(evateamclient.EntityRelation).
  Where(sq.Eq{evateamclient.TaskLinkFieldOutLink: "CmfTask:uuid"}).
  Limit(100)
links, meta, err := client.TaskLinksListQuery(ctx, qb)

func (*Client) TaskLinksOutgoing

func (c *Client) TaskLinksOutgoing(
	ctx context.Context,
	taskID string,
	fields []string,
) ([]models.TaskLink, *models.Meta, error)

TaskLinksOutgoing retrieves links where task is source (outgoing) Example:

links, meta, err := client.TaskLinksOutgoing(ctx, "CmfTask:uuid", nil)

func (*Client) TaskQuery

func (c *Client) TaskQuery(ctx context.Context, qb *QueryBuilder) (*models.Task, *models.Meta, error)

TaskQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name", "responsible", "executors").
  From(evateamclient.EntityTask).
  Where(sq.Eq{"code": "PROJ-123"})
task, meta, err := client.TaskQuery(ctx, qb)

func (*Client) TaskTimeLogs

func (c *Client) TaskTimeLogs(
	ctx context.Context,
	taskID string,
	fields []string,
) ([]models.TimeLog, *models.Meta, error)

TaskTimeLogs retrieves ALL time entries for specific task Example:

logs, meta, err := client.TaskTimeLogs(ctx, "CmfTask:uuid", nil)

func (*Client) TaskUpdate

func (c *Client) TaskUpdate(
	ctx context.Context,
	taskID string,
	updates map[string]any,
) (*models.Task, error)

TaskUpdate updates an existing task Example:

updates := map[string]any{
  "name":     "Updated Task Name",
  "priority": 5,
}
task, err := client.TaskUpdate(ctx, "CmfTask:uuid", updates)

func (*Client) TaskUpdateStatus

func (c *Client) TaskUpdateStatus(
	ctx context.Context,
	taskID string,
	status string,
) (*models.Task, error)

TaskUpdateStatus updates task status (workflow transition) Example:

task, err := client.TaskUpdateStatus(ctx, "CmfTask:uuid", "CLOSED")

func (*Client) Tasks

func (c *Client) Tasks(ctx context.Context, kwargs map[string]any) ([]models.TaskBrowse, *models.Meta, error)

Tasks retrieves tasks with custom filters (backward compatible, deprecated) Recommended: use TasksList with NewQueryBuilder() instead

func (*Client) TasksCount

func (c *Client) TasksCount(ctx context.Context, kwargs map[string]any) (int64, *models.Meta, error)

TasksCount returns total tasks matching filters.

func (*Client) TasksList

func (c *Client) TasksList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.TaskBrowse, *models.Meta, error)

TasksList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "code", "name", "priority", "cache_status_type").
  From(evateamclient.EntityTask).
  Where(sq.Eq{"project_id": "Project:uuid"}).
  Where(sq.Eq{"cache_status_type": evateamclient.StatusTypeOpen}).
  OrderBy("-priority", "name").
  Offset(0).Limit(100)
tasks, meta, err := client.TasksList(ctx, qb)

func (*Client) TimeLog

func (c *Client) TimeLog(
	ctx context.Context,
	timeLogID string,
	fields []string,
) (*models.TimeLog, *models.Meta, error)

TimeLog retrieves a single time log entry by ID (backward compatible) Example:

log, meta, err := client.TimeLog(ctx, "CmfTimeTrackerHistory:uuid", nil)

func (*Client) TimeLogCount

func (c *Client) TimeLogCount(
	ctx context.Context,
	qb *QueryBuilder,
) (int, error)

TimeLogCount counts using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  From(evateamclient.EntityTimeLog).
  Where(sq.Eq{"parent": "CmfTask:uuid"})
count, err := client.TimeLogCount(ctx, qb)

func (*Client) TimeLogCreate

func (c *Client) TimeLogCreate(
	ctx context.Context,
	params TimeLogCreateParams,
) (*models.TimeLog, error)

TimeLogCreate creates a new time log entry Example:

log, err := client.TimeLogCreate(ctx, TimeLogCreateParams{
  ParentID:  "CmfTask:uuid",
  TimeSpent: 180, // 3 hours in minutes
})

func (*Client) TimeLogDelete

func (c *Client) TimeLogDelete(
	ctx context.Context,
	timeLogID string,
) error

TimeLogDelete deletes a time log entry by ID Example:

err := client.TimeLogDelete(ctx, "CmfTimeTrackerHistory:uuid")

func (*Client) TimeLogQuery

func (c *Client) TimeLogQuery(ctx context.Context, qb *QueryBuilder) (*models.TimeLog, *models.Meta, error)

TimeLogQuery executes query using REAL Squirrel API Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "time_spent", "author", "description").
  From(evateamclient.EntityTimeLog).
  Where(sq.Eq{"id": "CmfTimeTrackerHistory:uuid"})
log, meta, err := client.TimeLogQuery(ctx, qb)

func (*Client) TimeLogUpdate

func (c *Client) TimeLogUpdate(
	ctx context.Context,
	timeLogID string,
	updates map[string]any,
) (*models.TimeLog, error)

TimeLogUpdate updates an existing time log entry Example:

updates := map[string]any{
  "time_spent": 240, // 4 hours in minutes
}
log, err := client.TimeLogUpdate(ctx, "CmfTimeTrackerHistory:uuid", updates)

func (*Client) TimeLogs

func (c *Client) TimeLogs(
	ctx context.Context,
	kwargs map[string]any,
) ([]models.TimeLog, *models.Meta, error)

TimeLogs retrieves time logs with custom filters (backward compatible, deprecated) Recommended: use TimeLogsList with NewQueryBuilder() instead

func (*Client) TimeLogsList

func (c *Client) TimeLogsList(
	ctx context.Context,
	qb *QueryBuilder,
) ([]models.TimeLog, *models.Meta, error)

TimeLogsList retrieves list using REAL Squirrel Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "time_spent", "author", "description").
  From(evateamclient.EntityTimeLog).
  Where(sq.Eq{"parent": "CmfTask:uuid"}).
  OrderBy("-cmf_created_at").
  Offset(0).Limit(100)
logs, meta, err := client.TimeLogsList(ctx, qb)

func (*Client) TimeSpentStats added in v1.2.1

func (c *Client) TimeSpentStats(ctx context.Context, params TimeSpentStatsParams) (*models.TimeSpentStats, error)

TimeSpentStats retrieves aggregated time spent report grouped by person and task.

func (*Client) UserComments

func (c *Client) UserComments(
	ctx context.Context,
	userID string,
	fields []string,
) ([]models.Comment, *models.Meta, error)

UserComments retrieves ALL comments by specific user Example:

comments, meta, err := client.UserComments(ctx, "Person:uuid", nil)

func (*Client) UserTaskTimeLogs

func (c *Client) UserTaskTimeLogs(
	ctx context.Context,
	taskID,
	userID string,
	fields []string,
) ([]models.TimeLog, *models.Meta, error)

UserTaskTimeLogs retrieves time entries for task by specific user Example:

logs, meta, err := client.UserTaskTimeLogs(ctx, "CmfTask:uuid", "CmfPerson:uuid", nil)

func (*Client) UserTimeLogs

func (c *Client) UserTimeLogs(
	ctx context.Context,
	userID string,
	fields []string,
) ([]models.TimeLog, *models.Meta, error)

UserTimeLogs retrieves ALL time entries by specific user Example:

logs, meta, err := client.UserTimeLogs(ctx, "CmfPerson:uuid", nil)

type Config

type Config struct {
	BaseURL  string
	APIToken string
	Debug    bool
	Timeout  time.Duration
}

Config holds client configuration

type DocumentCreateParams

type DocumentCreateParams struct {
	Name      string `json:"name"`
	ProjectID string `json:"project_id"`
	Text      string `json:"text,omitempty"`
	ParentID  string `json:"parent_id,omitempty"`
}

DocumentCreateParams contains parameters for creating a new document

type HTTPClient

type HTTPClient interface {
	Post(ctx context.Context, body []byte, url string) (*req.Response, error)
}

type ListCreateParams

type ListCreateParams struct {
	Name      string `json:"name"`
	ParentID  string `json:"parent_id"` // project ID (CmfProject:uuid)
	Code      string `json:"code,omitempty"`
	StartDate string `json:"start_date,omitempty"`
	EndDate   string `json:"end_date,omitempty"`
	Goal      string `json:"goal,omitempty"`
}

ListCreateParams contains parameters for creating a new list (sprint/release)

type Logger

type Logger interface {
	Debug(ctx context.Context, msg string, args ...any)
	Info(ctx context.Context, msg string, args ...any)
	Warn(ctx context.Context, msg string, args ...any)
	Error(ctx context.Context, msg string, args ...any)
}

Logger defines the logging interface. Compatible with slog, logrus, zap, and zerolog through adapters.

type Metrics

type Metrics interface {
	RecordRequestDuration(status int, method, host, function string, duration float64)
}

type Option

type Option func(*Client)

func WithDebug

func WithDebug(debug bool) Option

func WithLogger

func WithLogger(l Logger) Option

func WithMetrics

func WithMetrics(m Metrics) Option

type ProjectCreateParams

type ProjectCreateParams struct {
	Code       string   `json:"code"`
	Name       string   `json:"name"`
	Text       string   `json:"text,omitempty"`
	WorkflowID string   `json:"workflow_id,omitempty"`
	Executors  []string `json:"executors,omitempty"`
	Admins     []string `json:"cmfprojectadmins,omitempty"`
}

ProjectCreateParams contains parameters for creating a new project

type PrometheusMetrics

type PrometheusMetrics struct {
	RequestDuration prometheus.HistogramVec
}

PrometheusMetrics holds Prometheus metrics for the eva.team client

func NewPrometheusMetrics

func NewPrometheusMetrics() *PrometheusMetrics

func (*PrometheusMetrics) RecordRequestDuration

func (m *PrometheusMetrics) RecordRequestDuration(status int, method, host, function string, duration float64)

RecordRequestDuration writes duration request with labels

func (*PrometheusMetrics) Register

func (m *PrometheusMetrics) Register(registerer prometheus.Registerer) error

Register registers metrics with Prometheus registry

func (*PrometheusMetrics) Unregister

func (m *PrometheusMetrics) Unregister(registerer prometheus.Registerer) bool

Unregister unregisters metrics from Prometheus registry

type QueryBuilder

type QueryBuilder struct {
	// contains filtered or unexported fields
}

QueryBuilder wraps Squirrel's SelectBuilder and converts to EVA API kwargs This allows using real Squirrel API with EVA Team backend

Example:

qb := evateamclient.NewQueryBuilder().
  Select("id", "name", "code").
  From(EntityProject).
  Where(sq.Eq{"code": "PROJ-123"}).
  OrderBy("-cmf_created_at").
  Limit(50)

projects, meta, err := client.ProjectsList(ctx, qb)

func NewQueryBuilder

func NewQueryBuilder() *QueryBuilder

NewQueryBuilder creates a new EVA-compatible Squirrel builder

func (*QueryBuilder) From

func (qb *QueryBuilder) From(table string) *QueryBuilder

From sets the entity type (required for EVA API method routing) Valid values: EntityProject, EntityTask, EntityDocument, etc. Example: qb.From(EntityProject)

func (*QueryBuilder) IncludeArchived

func (qb *QueryBuilder) IncludeArchived() *QueryBuilder

IncludeArchived includes deleted/archived objects (EVA-specific) Example: qb.Where(sq.Eq{"cmf_deleted": true}).IncludeArchived()

func (*QueryBuilder) Limit

func (qb *QueryBuilder) Limit(limit uint64) *QueryBuilder

Limit sets maximum number of results Example: qb.Limit(50)

func (*QueryBuilder) NoMeta

func (qb *QueryBuilder) NoMeta() *QueryBuilder

NoMeta disables meta response (EVA-specific, faster queries) Example: qb.NoMeta() // Skip metadata for better performance

func (*QueryBuilder) Offset

func (qb *QueryBuilder) Offset(offset uint64) *QueryBuilder

Offset sets result offset for pagination Example: qb.Offset(100).Limit(50) // Skip 100, take 50

func (*QueryBuilder) OrderBy

func (qb *QueryBuilder) OrderBy(orderBys ...string) *QueryBuilder

OrderBy adds sorting Use "-field" prefix for DESC order, "field" for ASC

Examples:

qb.OrderBy("name")                    // ASC
qb.OrderBy("-cmf_created_at")        // DESC
qb.OrderBy("-priority", "name")      // Multiple columns

func (*QueryBuilder) Select

func (qb *QueryBuilder) Select(columns ...string) *QueryBuilder

Select sets columns to retrieve (maps to EVA "fields") If no columns provided, default fields will be applied by the caller. Example: qb.Select("id", "name", "code", "executors")

func (*QueryBuilder) String

func (qb *QueryBuilder) String() string

String returns human-readable query representation (for debugging)

func (*QueryBuilder) ToKwargs

func (qb *QueryBuilder) ToKwargs() (map[string]any, error)

ToKwargs converts Squirrel SelectBuilder to EVA API kwargs This translates SQL-like queries to JSON-RPC BQL format

Returns map with keys: filter, fields, order_by, slice, include_archived, no_meta

func (*QueryBuilder) ToMethod

func (qb *QueryBuilder) ToMethod(single bool) (string, error)

ToMethod returns the appropriate EVA API method based on table Example: "CmfProject" -> "CmfProject.list" or "CmfProject.get" if single is true

func (*QueryBuilder) Validate

func (qb *QueryBuilder) Validate() error

func (*QueryBuilder) Where

func (qb *QueryBuilder) Where(pred any) *QueryBuilder

Where adds filter conditions using Squirrel predicates Multiple Where() calls are combined with AND logic

Examples:

qb.Where(sq.Eq{"code": "PROJ-123"})
qb.Where(sq.Gt{"priority": 3})
qb.Where(sq.Like{"name": "%Mobile%"})
qb.Where(sq.And{sq.Eq{"system": false}, sq.GtOrEq{"created_at": "2024-01-01"}})

type RPCError

type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

RPCError represents JSON-RPC error response

func (*RPCError) Error

func (e *RPCError) Error() string

type RPCRequest

type RPCRequest struct {
	JSONRPC string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	CallID  string      `json:"callid"`
	Args    interface{} `json:"args,omitempty"`
	Kwargs  interface{} `json:"kwargs,omitempty"`
}

type SprintExecutorsKPIParams added in v1.2.1

type SprintExecutorsKPIParams struct {
	// SprintCode is optional (example: "SPR-001543").
	SprintCode string
	// ProjectCode is required; if set, must match project.
	ProjectCode string
	// SprintStartDate optionally.
	SprintStartDate time.Time
	// SprintEndDate optionally.
	SprintEndDate time.Time
}

SprintExecutorsKPIParams contains KPI report settings for sprint executors.

type TaskCreateParams

type TaskCreateParams struct {
	Name        string   `json:"name"`
	ProjectID   string   `json:"project_id"`
	Text        string   `json:"text,omitempty"`
	Priority    int      `json:"priority,omitempty"`
	Deadline    string   `json:"deadline,omitempty"`
	Responsible string   `json:"responsible,omitempty"`
	Executors   []string `json:"executors,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	Lists       []string `json:"lists,omitempty"` // sprints
	Epic        string   `json:"epic,omitempty"`
	ParentTask  string   `json:"parent_task,omitempty"`
	LogicTypeID string   `json:"logic_type_id,omitempty"`
}

TaskCreateParams contains parameters for creating a new task.

ProjectID accepts a project ID (e.g. "CmfProject:uuid") or a project code (e.g. "epud") — both are valid for the server's `parent` field. Epic and ParentTask accept TASK CODES (e.g. "TASK-000777", "EPC-000123"), not IDs, per the EVA API contract. LogicTypeID accepts a LogicType ID (e.g. "CmfLogicType:uuid"); use LogicTypeByCode to resolve a code to an ID.

type TimeLogCreateParams

type TimeLogCreateParams struct {
	ParentID  string `json:"parent_id"`  // task ID (CmfTask:uuid)
	TimeSpent int    `json:"time_spent"` // minutes
}

TimeLogCreateParams contains parameters for creating a new time log entry

type TimeSpentStatsParams added in v1.2.1

type TimeSpentStatsParams struct {
	ProjectID string // required: "CmfProject:uuid"
	DateFrom  string // optional: "2025-01-01"
	DateTo    string // optional: "2025-12-31"
}

TimeSpentStatsParams contains parameters for time spent stats aggregation.

Directories

Path Synopsis
pkg
evateamclient-mcp command
Package main provides the MCP server for EVA Team API.
Package main provides the MCP server for EVA Team API.
evateamclient-mcp/tools
Package tools provides MCP tool handlers for EVA Team API.
Package tools provides MCP tool handlers for EVA Team API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL