evateamclient

package module
v1.2.1 Latest Latest
Warning

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

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

README

EVA Team Go Client Library

Test Coverage GitHub Release

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
  • 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
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)

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
Available Tools

The MCP server provides 55+ 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
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
Example Prompts

Once configured, you can ask Claude:

  • "Show me all open tasks in project MYPROJ"
  • "Create a new task in project MYPROJ with name 'Fix login bug'"
  • "Get statistics for sprint SPR-001543"
  • "List all comments on task MYPROJ-123"
  • "Log 2 hours on task MYPROJ-456"

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"
)
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 (
	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 (
	// Core fields
	TaskFieldID              = "id"
	TaskFieldClassName       = "class_name"
	TaskFieldCode            = "code"
	TaskFieldName            = "name"
	TaskFieldText            = "text"
	TaskFieldProjectID       = "project_id"
	TaskFieldParentID        = "parent_id"
	TaskFieldParentTask      = "parent_task"
	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,
	}
)

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) 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) 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) 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
	EpicID      string   `json:"epic_id,omitempty"`
	LogicTypeID string   `json:"logic_type_id,omitempty"`
}

TaskCreateParams contains parameters for creating a new task

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