activeproject 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_project/adapters/base.rb +6 -0
- data/lib/active_project/adapters/basecamp/issues.rb +25 -6
- data/lib/active_project/adapters/jira/issues.rb +31 -13
- data/lib/active_project/adapters/jira_adapter.rb +5 -3
- data/lib/active_project/adapters/trello/issues.rb +26 -10
- data/lib/active_project/adapters/trello_adapter.rb +5 -1
- data/lib/active_project/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9960da80d5e32d0cdfa4fce3ffe824ebf3c36a8710d0a7782dbac3ec7001b863
|
4
|
+
data.tar.gz: 1ca92b2e165afbe314b70d970cb42817e5987ae3bfaa5a7d0ba58987999afe45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46a0f331e8f7e956d643e6070d21dc20abe3825308c0761067df3773b3a20fcea7b94aa9c07f333c0fc794443945419436486b3daec49e4a19b5e473b05b4c6c
|
7
|
+
data.tar.gz: d78685242c2ea12a5a522a74e64b8f438d613dd2bb2977c14aca0ed7ba918ef953719f17b23b8cf6400723ea23886f7a63df898a9b804214eab4db635c8fb794
|
@@ -75,6 +75,12 @@ module ActiveProject
|
|
75
75
|
raise NotImplementedError, "#{self.class.name} must implement #update_issue"
|
76
76
|
end
|
77
77
|
|
78
|
+
# Base implementation of delete_issue that raises NotImplementedError
|
79
|
+
# This will be included in the base adapter class and overridden by specific adapters
|
80
|
+
def delete_issue(id, context = {})
|
81
|
+
raise NotImplementedError, "The #{self.class.name} adapter does not implement delete_issue"
|
82
|
+
end
|
83
|
+
|
78
84
|
# Adds a comment to an issue.
|
79
85
|
# @param issue_id [String, Integer] The ID or key of the issue.
|
80
86
|
# @param comment_body [String] The text of the comment.
|
@@ -6,11 +6,12 @@ module ActiveProject
|
|
6
6
|
module Issues
|
7
7
|
# Lists To-dos within a specific project.
|
8
8
|
# @param project_id [String, Integer] The ID of the Basecamp project.
|
9
|
-
# @param options [Hash] Optional options. Accepts :todolist_id.
|
9
|
+
# @param options [Hash] Optional options. Accepts :todolist_id and :page_size.
|
10
10
|
# @return [Array<ActiveProject::Resources::Issue>] An array of issue resources.
|
11
11
|
def list_issues(project_id, options = {})
|
12
12
|
all_todos = []
|
13
13
|
todolist_id = options[:todolist_id]
|
14
|
+
page_size = options[:page_size] || 50
|
14
15
|
|
15
16
|
unless todolist_id
|
16
17
|
todolist_id = find_first_todolist_id(project_id)
|
@@ -18,14 +19,16 @@ module ActiveProject
|
|
18
19
|
end
|
19
20
|
|
20
21
|
path = "buckets/#{project_id}/todolists/#{todolist_id}/todos.json"
|
22
|
+
query = {}
|
23
|
+
query[:per_page] = page_size if page_size
|
21
24
|
|
22
25
|
loop do
|
23
|
-
response = @connection.get(path)
|
26
|
+
response = @connection.get(path, query)
|
24
27
|
todos_data = begin
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
JSON.parse(response.body)
|
29
|
+
rescue StandardError
|
30
|
+
[]
|
31
|
+
end
|
29
32
|
break if todos_data.empty?
|
30
33
|
|
31
34
|
todos_data.each do |todo_data|
|
@@ -37,6 +40,7 @@ module ActiveProject
|
|
37
40
|
break unless next_url
|
38
41
|
|
39
42
|
path = next_url.sub(@base_url, "").sub(%r{^/}, "")
|
43
|
+
query = {} # Clear query as pagination is in the URL now
|
40
44
|
end
|
41
45
|
|
42
46
|
all_todos
|
@@ -133,6 +137,21 @@ module ActiveProject
|
|
133
137
|
|
134
138
|
find_issue(todo_id, context)
|
135
139
|
end
|
140
|
+
|
141
|
+
# Deletes a To-do in Basecamp.
|
142
|
+
# @param todo_id [String, Integer] The ID of the Basecamp To-do to delete.
|
143
|
+
# @param context [Hash] Required context: { project_id: '...' }.
|
144
|
+
# @return [Boolean] True if successfully deleted.
|
145
|
+
def delete_issue(todo_id, context = {})
|
146
|
+
project_id = context[:project_id]
|
147
|
+
unless project_id
|
148
|
+
raise ArgumentError, "Missing required context: :project_id must be provided for BasecampAdapter#delete_issue"
|
149
|
+
end
|
150
|
+
|
151
|
+
path = "buckets/#{project_id}/todos/#{todo_id}.json"
|
152
|
+
make_request(:delete, path)
|
153
|
+
true
|
154
|
+
end
|
136
155
|
end
|
137
156
|
end
|
138
157
|
end
|
@@ -4,14 +4,17 @@ module ActiveProject
|
|
4
4
|
module Adapters
|
5
5
|
module Jira
|
6
6
|
module Issues
|
7
|
+
DEFAULT_FIELDS = %w[summary description status assignee reporter created updated project issuetype duedate priority].freeze
|
8
|
+
|
7
9
|
# Lists issues within a specific project, optionally filtered by JQL.
|
8
10
|
# @param project_id_or_key [String, Integer] The ID or key of the project.
|
9
|
-
# @param options [Hash] Optional filtering/pagination options.
|
11
|
+
# @param options [Hash] Optional filtering/pagination options. Accepts :jql, :fields, :start_at, :max_results.
|
10
12
|
# @return [Array<ActiveProject::Resources::Issue>]
|
11
13
|
def list_issues(project_id_or_key, options = {})
|
12
14
|
start_at = options.fetch(:start_at, 0)
|
13
15
|
max_results = options.fetch(:max_results, 50)
|
14
16
|
jql = options.fetch(:jql, "project = '#{project_id_or_key}' ORDER BY created DESC")
|
17
|
+
fields = options[:fields] || DEFAULT_FIELDS
|
15
18
|
|
16
19
|
all_issues = []
|
17
20
|
path = "/rest/api/3/search"
|
@@ -20,8 +23,7 @@ module ActiveProject
|
|
20
23
|
jql: jql,
|
21
24
|
startAt: start_at,
|
22
25
|
maxResults: max_results,
|
23
|
-
fields:
|
24
|
-
issuetype duedate priority]
|
26
|
+
fields: fields
|
25
27
|
}.to_json
|
26
28
|
|
27
29
|
response_data = make_request(:post, path, payload)
|
@@ -36,11 +38,12 @@ module ActiveProject
|
|
36
38
|
|
37
39
|
# Finds a specific issue by its ID or key using the V3 endpoint.
|
38
40
|
# @param id_or_key [String, Integer] The ID or key of the issue.
|
39
|
-
# @param context [Hash] Optional context
|
41
|
+
# @param context [Hash] Optional context. Accepts :fields for field selection.
|
40
42
|
# @return [ActiveProject::Resources::Issue]
|
41
|
-
def find_issue(id_or_key,
|
42
|
-
fields =
|
43
|
-
|
43
|
+
def find_issue(id_or_key, context = {})
|
44
|
+
fields = context[:fields] || DEFAULT_FIELDS
|
45
|
+
fields_param = fields.is_a?(Array) ? fields.join(",") : fields
|
46
|
+
path = "/rest/api/3/issue/#{id_or_key}?fields=#{fields_param}"
|
44
47
|
|
45
48
|
issue_data = make_request(:get, path)
|
46
49
|
map_issue_data(issue_data)
|
@@ -54,8 +57,8 @@ module ActiveProject
|
|
54
57
|
path = "/rest/api/3/issue"
|
55
58
|
|
56
59
|
unless attributes[:project].is_a?(Hash) && (attributes[:project][:id] || attributes[:project][:key]) &&
|
57
|
-
|
58
|
-
|
60
|
+
attributes[:summary] && !attributes[:summary].empty? &&
|
61
|
+
attributes[:issue_type] && (attributes[:issue_type][:id] || attributes[:issue_type][:name])
|
59
62
|
raise ArgumentError,
|
60
63
|
"Missing required attributes for issue creation: :project (must be a Hash with id/key), :summary, :issue_type (with id/name)"
|
61
64
|
end
|
@@ -83,6 +86,8 @@ module ActiveProject
|
|
83
86
|
|
84
87
|
fields_payload[:priority] = attributes[:priority] if attributes.key?(:priority)
|
85
88
|
|
89
|
+
fields_payload[:parent] = attributes[:parent] if attributes.key?(:parent)
|
90
|
+
|
86
91
|
payload = { fields: fields_payload }.to_json
|
87
92
|
response_data = make_request(:post, path, payload)
|
88
93
|
|
@@ -92,9 +97,9 @@ module ActiveProject
|
|
92
97
|
# Updates an existing issue in Jira using the V3 endpoint.
|
93
98
|
# @param id_or_key [String, Integer] The ID or key of the issue to update.
|
94
99
|
# @param attributes [Hash] Issue attributes to update (e.g., :summary, :description, :assignee_id, :due_on, :priority).
|
95
|
-
# @param context [Hash] Optional context
|
100
|
+
# @param context [Hash] Optional context. Accepts :fields for field selection on return.
|
96
101
|
# @return [ActiveProject::Resources::Issue]
|
97
|
-
def update_issue(id_or_key, attributes,
|
102
|
+
def update_issue(id_or_key, attributes, context = {})
|
98
103
|
path = "/rest/api/3/issue/#{id_or_key}"
|
99
104
|
|
100
105
|
update_fields = {}
|
@@ -119,12 +124,25 @@ module ActiveProject
|
|
119
124
|
|
120
125
|
update_fields[:priority] = attributes[:priority] if attributes.key?(:priority)
|
121
126
|
|
122
|
-
return find_issue(id_or_key) if update_fields.empty?
|
127
|
+
return find_issue(id_or_key, context) if update_fields.empty?
|
123
128
|
|
124
129
|
payload = { fields: update_fields }.to_json
|
125
130
|
make_request(:put, path, payload)
|
126
131
|
|
127
|
-
find_issue(id_or_key)
|
132
|
+
find_issue(id_or_key, context)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Deletes an issue from Jira.
|
136
|
+
# @param id_or_key [String, Integer] The ID or key of the issue to delete.
|
137
|
+
# @param context [Hash] Optional context. Accepts :delete_subtasks to indicate whether subtasks should be deleted.
|
138
|
+
# @return [Boolean] True if successfully deleted.
|
139
|
+
def delete_issue(id_or_key, context = {})
|
140
|
+
delete_subtasks = context[:delete_subtasks] || false
|
141
|
+
path = "/rest/api/3/issue/#{id_or_key}"
|
142
|
+
query = { deleteSubtasks: delete_subtasks }
|
143
|
+
|
144
|
+
make_request(:delete, path, nil, query)
|
145
|
+
true
|
128
146
|
end
|
129
147
|
end
|
130
148
|
end
|
@@ -57,8 +57,10 @@ module ActiveProject
|
|
57
57
|
# Initializes the Faraday connection object.
|
58
58
|
|
59
59
|
# Makes an HTTP request. Returns parsed JSON or raises appropriate error.
|
60
|
-
def make_request(method, path, body = nil)
|
61
|
-
response = @connection.run_request(method, path, body, nil)
|
60
|
+
def make_request(method, path, body = nil, query = nil)
|
61
|
+
response = @connection.run_request(method, path, body, nil) do |req|
|
62
|
+
req.params = query if query # Add query params to the request
|
63
|
+
end
|
62
64
|
|
63
65
|
# Check for AUTHENTICATED_FAILED header even on 200 OK
|
64
66
|
if response.status == 200 && response.headers["x-seraph-loginreason"]&.include?("AUTHENTICATED_FAILED")
|
@@ -78,7 +80,7 @@ module ActiveProject
|
|
78
80
|
status = e.response&.status
|
79
81
|
body = e.response&.body
|
80
82
|
raise ApiError.new("Jira API connection error (Status: #{status || 'N/A'}): #{e.message}", original_error: e,
|
81
|
-
|
83
|
+
status_code: status, response_body: body)
|
82
84
|
end
|
83
85
|
|
84
86
|
# Handles Faraday errors based on the response object (for non-2xx responses).
|
@@ -4,13 +4,17 @@ module ActiveProject
|
|
4
4
|
module Adapters
|
5
5
|
module Trello
|
6
6
|
module Issues
|
7
|
+
DEFAULT_FIELDS = %w[id name desc closed idList idBoard due dueComplete idMembers].freeze
|
8
|
+
|
7
9
|
# Lists Trello cards on a specific board.
|
8
10
|
# @param board_id [String] The ID of the Trello board.
|
9
|
-
# @param options [Hash] Optional filtering options.
|
11
|
+
# @param options [Hash] Optional filtering options. Accepts :filter and :fields.
|
10
12
|
# @return [Array<ActiveProject::Resources::Issue>]
|
11
13
|
def list_issues(board_id, options = {})
|
12
14
|
path = "boards/#{board_id}/cards"
|
13
|
-
|
15
|
+
|
16
|
+
fields = options[:fields] ? Array(options[:fields]).join(",") : DEFAULT_FIELDS.join(",")
|
17
|
+
query = { fields: fields, list: true }
|
14
18
|
query[:filter] = options[:filter] if options[:filter]
|
15
19
|
|
16
20
|
cards_data = make_request(:get, path, nil, query)
|
@@ -21,11 +25,14 @@ module ActiveProject
|
|
21
25
|
|
22
26
|
# Finds a specific Card by its ID.
|
23
27
|
# @param card_id [String] The ID of the Trello Card.
|
24
|
-
# @param context [Hash] Optional context
|
28
|
+
# @param context [Hash] Optional context. Accepts :fields for specific field selection.
|
25
29
|
# @return [ActiveProject::Resources::Issue]
|
26
|
-
def find_issue(card_id,
|
30
|
+
def find_issue(card_id, context = {})
|
27
31
|
path = "cards/#{card_id}"
|
28
|
-
|
32
|
+
|
33
|
+
fields = context[:fields] ? Array(context[:fields]).join(",") : DEFAULT_FIELDS.join(",")
|
34
|
+
query = { fields: fields, list: true }
|
35
|
+
|
29
36
|
card_data = make_request(:get, path, nil, query)
|
30
37
|
map_card_data(card_data, card_data["idBoard"])
|
31
38
|
end
|
@@ -58,7 +65,7 @@ module ActiveProject
|
|
58
65
|
# Updates an existing Card in Trello.
|
59
66
|
# @param card_id [String] The ID of the Trello Card.
|
60
67
|
# @param attributes [Hash] Attributes to update (e.g., :title, :description, :list_id, :closed, :due_on, :assignee_ids, :status).
|
61
|
-
# @param context [Hash] Optional context
|
68
|
+
# @param context [Hash] Optional context. Accepts :fields for return data field selection.
|
62
69
|
# @return [ActiveProject::Resources::Issue]
|
63
70
|
def update_issue(card_id, attributes, context = {})
|
64
71
|
update_attributes = attributes.dup
|
@@ -67,10 +74,10 @@ module ActiveProject
|
|
67
74
|
target_status = update_attributes.delete(:status)
|
68
75
|
|
69
76
|
board_id = update_attributes[:board_id] || begin
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
77
|
+
find_issue(card_id).project_id
|
78
|
+
rescue NotFoundError
|
79
|
+
raise NotFoundError, "Trello card with ID '#{card_id}' not found."
|
80
|
+
end
|
74
81
|
|
75
82
|
unless board_id
|
76
83
|
raise ApiError, "Could not determine board ID for card '#{card_id}' to perform status mapping."
|
@@ -111,6 +118,15 @@ module ActiveProject
|
|
111
118
|
card_data = make_request(:put, path, nil, query_params.compact)
|
112
119
|
map_card_data(card_data, card_data["idBoard"])
|
113
120
|
end
|
121
|
+
|
122
|
+
# Deletes a Trello card.
|
123
|
+
# @param card_id [String] The ID of the Trello Card to delete.
|
124
|
+
# @return [Boolean] True if successfully deleted.
|
125
|
+
def delete_issue(card_id, **)
|
126
|
+
path = "cards/#{card_id}"
|
127
|
+
make_request(:delete, path)
|
128
|
+
true
|
129
|
+
end
|
114
130
|
end
|
115
131
|
end
|
116
132
|
end
|
@@ -96,7 +96,11 @@ module ActiveProject
|
|
96
96
|
def handle_faraday_error(error)
|
97
97
|
status = error.response_status
|
98
98
|
body = error.response_body
|
99
|
-
|
99
|
+
body = JSON.parse(body) if body.is_a?(String) && !body.empty? rescue body
|
100
|
+
if body.is_a?(Hash)
|
101
|
+
message = body["message"]
|
102
|
+
end
|
103
|
+
message ||= body || "Unknown Trello Error"
|
100
104
|
|
101
105
|
case status
|
102
106
|
when 401, 403
|
metadata
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeproject
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abdelkader Boudih
|
8
|
+
autorequire:
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
11
|
date: 2025-04-10 00:00:00.000000000 Z
|
@@ -125,6 +126,7 @@ licenses:
|
|
125
126
|
metadata:
|
126
127
|
homepage_uri: https://github.com/seuros/active_project
|
127
128
|
source_code_uri: https://github.com/seuros/active_project
|
129
|
+
post_install_message:
|
128
130
|
rdoc_options: []
|
129
131
|
require_paths:
|
130
132
|
- lib
|
@@ -139,7 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
141
|
- !ruby/object:Gem::Version
|
140
142
|
version: '0'
|
141
143
|
requirements: []
|
142
|
-
rubygems_version: 3.
|
144
|
+
rubygems_version: 3.5.22
|
145
|
+
signing_key:
|
143
146
|
specification_version: 4
|
144
147
|
summary: A standardized Ruby interface for multiple project management APIs (Jira,
|
145
148
|
Basecamp, Trello, etc.).
|