Module: ActiveProject::Adapters::Basecamp::Issues

Included in:
ActiveProject::Adapters::BasecampAdapter
Defined in:
lib/active_project/adapters/basecamp/issues.rb

Instance Method Summary collapse

Instance Method Details

#create_issue(project_id, attributes) ⇒ ActiveProject::Resources::Issue

Creates a new To-do in Basecamp.

Parameters:

  • project_id (String, Integer)

    The ID of the Basecamp project.

  • attributes (Hash)

    To-do attributes. Required: :todolist_id, :title. Optional: :description, :due_on, :assignee_ids.

Returns:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/active_project/adapters/basecamp/issues.rb', line 70

def create_issue(project_id, attributes)
  todolist_id = attributes[:todolist_id]
  title = attributes[:title]

  unless todolist_id && title && !title.empty?
    raise ArgumentError, "Missing required attributes for Basecamp to-do creation: :todolist_id, :title"
  end

  path = "buckets/#{project_id}/todolists/#{todolist_id}/todos.json"

  payload = {
    content: title,
    description: attributes[:description],
    due_on: attributes[:due_on].respond_to?(:strftime) ? attributes[:due_on].strftime("%Y-%m-%d") : attributes[:due_on],
    assignee_ids: attributes[:assignee_ids]
  }.compact

  todo_data = make_request(:post, path, payload.to_json)
  map_todo_data(todo_data, project_id)
end

#delete_issue(todo_id, context = {}) ⇒ Boolean

Deletes a To-do in Basecamp.

Parameters:

  • todo_id (String, Integer)

    The ID of the Basecamp To-do to delete.

  • context (Hash) (defaults to: {})

    Required context: { project_id: ‘…’ }.

Returns:

  • (Boolean)

    True if successfully deleted.



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/active_project/adapters/basecamp/issues.rb', line 145

def delete_issue(todo_id, context = {})
  project_id = context[:project_id]
  unless project_id
    raise ArgumentError,
          "Missing required context: :project_id must be provided for BasecampAdapter#delete_issue"
  end

  path = "buckets/#{project_id}/todos/#{todo_id}.json"
  make_request(:delete, path)
  true
end

#find_issue(todo_id, context = {}) ⇒ ActiveProject::Resources::Issue

Finds a specific To-do by its ID.

Parameters:

  • todo_id (String, Integer)

    The ID of the Basecamp To-do.

  • context (Hash) (defaults to: {})

    Required context: { project_id: ‘…’ }.

Returns:



55
56
57
58
59
60
61
62
63
64
# File 'lib/active_project/adapters/basecamp/issues.rb', line 55

def find_issue(todo_id, context = {})
  project_id = context[:project_id]
  unless project_id
    raise ArgumentError, "Missing required context: :project_id must be provided for BasecampAdapter#find_issue"
  end

  path = "buckets/#{project_id}/todos/#{todo_id}.json"
  todo_data = make_request(:get, path)
  map_todo_data(todo_data, project_id)
end

#list_issues(project_id, options = {}) ⇒ Array<ActiveProject::Resources::Issue>

Lists To-dos within a specific project.

Parameters:

  • project_id (String, Integer)

    The ID of the Basecamp project.

  • options (Hash) (defaults to: {})

    Optional options. Accepts :todolist_id and :page_size.

Returns:



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/active_project/adapters/basecamp/issues.rb', line 11

def list_issues(project_id, options = {})
  all_todos = []
  todolist_id = options[:todolist_id]
  page_size = options[:page_size] || 50

  unless todolist_id
    todolist_id = find_first_todolist_id(project_id)
    return [] unless todolist_id
  end

  path = "buckets/#{project_id}/todolists/#{todolist_id}/todos.json"
  query = {}
  query[:per_page] = page_size if page_size

  loop do
    response = @connection.get(path, query)
    todos_data = begin
      JSON.parse(response.body)
    rescue StandardError
      []
    end
    break if todos_data.empty?

    todos_data.each do |todo_data|
      all_todos << map_todo_data(todo_data, project_id)
    end

    link_header = response.headers["Link"]
    next_url = parse_next_link(link_header)
    break unless next_url

    path = next_url.sub(@base_url, "").sub(%r{^/}, "")
    query = {} # Clear query as pagination is in the URL now
  end

  all_todos
rescue Faraday::Error => e
  handle_faraday_error(e)
end

#update_issue(todo_id, attributes, context = {}) ⇒ ActiveProject::Resources::Issue

Updates an existing To-do in Basecamp. Handles updates to standard fields via PUT and status changes via POST/DELETE completion endpoints.

Parameters:

  • todo_id (String, Integer)

    The ID of the Basecamp To-do.

  • attributes (Hash)

    Attributes to update (e.g., :title, :description, :status, :assignee_ids, :due_on).

  • context (Hash) (defaults to: {})

    Required context: { project_id: ‘…’ }.

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/active_project/adapters/basecamp/issues.rb', line 97

def update_issue(todo_id, attributes, context = {})
  project_id = context[:project_id]
  unless project_id
    raise ArgumentError,
          "Missing required context: :project_id must be provided for BasecampAdapter#update_issue"
  end

  put_payload = {}
  put_payload[:content] = attributes[:title] if attributes.key?(:title)
  put_payload[:description] = attributes[:description] if attributes.key?(:description)
  if attributes.key?(:due_on)
    due_on_val = attributes[:due_on]
    put_payload[:due_on] = due_on_val.respond_to?(:strftime) ? due_on_val.strftime("%Y-%m-%d") : due_on_val
  end
  put_payload[:assignee_ids] = attributes[:assignee_ids] if attributes.key?(:assignee_ids)

  status_change_required = attributes.key?(:status)
  target_status = attributes[:status] if status_change_required

  unless !put_payload.empty? || status_change_required
    raise ArgumentError, "No attributes provided to update for BasecampAdapter#update_issue"
  end

  unless put_payload.empty?
    put_path = "buckets/#{project_id}/todos/#{todo_id}.json"
    make_request(:put, put_path, put_payload.compact.to_json)
  end

  if status_change_required
    completion_path = "buckets/#{project_id}/todos/#{todo_id}/completion.json"
    begin
      if target_status == :closed
        make_request(:post, completion_path)
      elsif target_status == :open
        make_request(:delete, completion_path)
      end
    rescue NotFoundError
      raise unless target_status == :open
    end
  end

  find_issue(todo_id, context)
end