class Attio::Task

Represents a task in Attio

def self.create(content: nil, format: "plaintext", **params)

Override create to handle required content parameter
def self.create(content: nil, format: "plaintext", **params)
  raise ArgumentError, "Content is required" if content.nil? || content.to_s.empty?
  request_params = {
    data: {
      content: content,  # API expects 'content'
      format: format,    # Format is required
      is_completed: params[:is_completed] || false,
      linked_records: params[:linked_records] || [],
      assignees: params[:assignees] || []
    }
  }
  # deadline_at must be present (null or valid date)
  request_params[:data][:deadline_at] = params[:deadline_at]
  # Remove the params that we've already included in request_params
  opts = params.except(:content, :format, :deadline_at, :is_completed, :linked_records, :assignees)
  response = execute_request(:POST, resource_path, request_params, opts)
  new(response["data"] || response, opts)
end

def self.list(**params)

Custom list implementation to handle query params properly
def self.list(**params)
  # Query params should be part of the request, not opts
  query_params = params.slice(:limit, :offset, :sort, :linked_object, :linked_record_id, :assignee, :is_completed)
  opts = params.except(:limit, :offset, :sort, :linked_object, :linked_record_id, :assignee, :is_completed)
  response = execute_request(:GET, resource_path, query_params, opts)
  ListObject.new(response, self, params, opts)
end

def self.resource_path

Returns:
  • (String) - The API path
def self.resource_path
  "tasks"
end

def self.update(id, **params)

Override update to use PATCH with data wrapper
def self.update(id, **params)
  validate_id!(id)
  request_params = {
    data: params.slice(:content, :format, :deadline_at, :is_completed, :linked_records, :assignees).compact
  }
  # Remove the params that we've already included in request_params
  opts = params.except(:content, :format, :deadline_at, :is_completed, :linked_records, :assignees)
  response = execute_request(:PATCH, "#{resource_path}/#{id}", request_params, opts)
  new(response["data"] || response, opts)
end

def complete!(**opts)

Convenience method to mark task as completed
def complete!(**opts)
  raise InvalidRequestError, "Cannot complete a task without an ID" unless persisted?
  params = {
    data: {
      is_completed: true
    }
  }
  response = self.class.send(:execute_request, :PATCH, resource_path, params, opts)
  update_from(response["data"] || response)
  self
end

def deadline_at

Parse deadline_at as Time
def deadline_at
  value = @attributes[:deadline_at]
  return nil if value.nil?
  case value
  when Time
    value
  when String
    Time.parse(value)
  else
    value
  end
end

def destroy(**opts)

Override destroy to use the correct task ID
def destroy(**opts)
  raise InvalidRequestError, "Cannot destroy a task without an ID" unless persisted?
  task_id = extract_task_id
  self.class.send(:execute_request, :DELETE, "#{self.class.resource_path}/#{task_id}", {}, opts)
  @attributes.clear
  @changed_attributes.clear
  @id = nil
  true
end

def extract_task_id

def extract_task_id
  case id
  when Hash
    id[:task_id] || id["task_id"]
  else
    id
  end
end

def resource_path

def resource_path
  task_id = extract_task_id
  "#{self.class.resource_path}/#{task_id}"
end

def save(**opts)

Override save to handle task-specific attributes
def save(**opts)
  raise InvalidRequestError, "Cannot save a task without an ID" unless persisted?
  params = {
    data: changed_attributes.slice(:content, :deadline_at, :is_completed, :linked_records, :assignees).compact
  }
  return self unless params[:data].any?
  response = self.class.send(:execute_request, :PATCH, resource_path, params, opts)
  update_from(response["data"] || response)
  reset_changes!
  self
end