activeproject 0.2.0 → 0.5.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/README.md +248 -51
- data/lib/active_project/adapters/base.rb +154 -14
- data/lib/active_project/adapters/basecamp/comments.rb +34 -0
- data/lib/active_project/adapters/basecamp/connection.rb +10 -23
- data/lib/active_project/adapters/basecamp/issues.rb +6 -5
- data/lib/active_project/adapters/basecamp/webhooks.rb +7 -8
- data/lib/active_project/adapters/basecamp_adapter.rb +2 -11
- data/lib/active_project/adapters/fizzy/columns.rb +116 -0
- data/lib/active_project/adapters/fizzy/comments.rb +129 -0
- data/lib/active_project/adapters/fizzy/connection.rb +41 -0
- data/lib/active_project/adapters/fizzy/issues.rb +221 -0
- data/lib/active_project/adapters/fizzy/projects.rb +105 -0
- data/lib/active_project/adapters/fizzy_adapter.rb +151 -0
- data/lib/active_project/adapters/github_project/comments.rb +91 -0
- data/lib/active_project/adapters/github_project/connection.rb +58 -0
- data/lib/active_project/adapters/github_project/helpers.rb +100 -0
- data/lib/active_project/adapters/github_project/issues.rb +287 -0
- data/lib/active_project/adapters/github_project/projects.rb +139 -0
- data/lib/active_project/adapters/github_project/webhooks.rb +168 -0
- data/lib/active_project/adapters/github_project.rb +8 -0
- data/lib/active_project/adapters/github_project_adapter.rb +65 -0
- data/lib/active_project/adapters/github_repo/connection.rb +62 -0
- data/lib/active_project/adapters/github_repo/issues.rb +242 -0
- data/lib/active_project/adapters/github_repo/projects.rb +116 -0
- data/lib/active_project/adapters/github_repo/webhooks.rb +354 -0
- data/lib/active_project/adapters/github_repo_adapter.rb +134 -0
- data/lib/active_project/adapters/jira/attribute_normalizer.rb +16 -0
- data/lib/active_project/adapters/jira/comments.rb +41 -0
- data/lib/active_project/adapters/jira/connection.rb +43 -24
- data/lib/active_project/adapters/jira/issues.rb +21 -7
- data/lib/active_project/adapters/jira/projects.rb +3 -1
- data/lib/active_project/adapters/jira/transitions.rb +2 -1
- data/lib/active_project/adapters/jira/webhooks.rb +5 -7
- data/lib/active_project/adapters/jira_adapter.rb +23 -30
- data/lib/active_project/adapters/trello/comments.rb +34 -0
- data/lib/active_project/adapters/trello/connection.rb +28 -21
- data/lib/active_project/adapters/trello/issues.rb +7 -5
- data/lib/active_project/adapters/trello/webhooks.rb +5 -7
- data/lib/active_project/adapters/trello_adapter.rb +5 -25
- data/lib/active_project/association_proxy.rb +3 -2
- data/lib/active_project/async.rb +9 -0
- data/lib/active_project/configuration.rb +6 -3
- data/lib/active_project/configurations/base_adapter_configuration.rb +102 -0
- data/lib/active_project/configurations/basecamp_configuration.rb +42 -0
- data/lib/active_project/configurations/fizzy_configuration.rb +47 -0
- data/lib/active_project/configurations/github_configuration.rb +57 -0
- data/lib/active_project/configurations/jira_configuration.rb +54 -0
- data/lib/active_project/configurations/trello_configuration.rb +24 -2
- data/lib/active_project/connections/base.rb +35 -0
- data/lib/active_project/connections/graph_ql.rb +83 -0
- data/lib/active_project/connections/http_client.rb +79 -0
- data/lib/active_project/connections/pagination.rb +44 -0
- data/lib/active_project/connections/rest.rb +33 -0
- data/lib/active_project/error_mapper.rb +38 -0
- data/lib/active_project/errors.rb +13 -0
- data/lib/active_project/railtie.rb +33 -0
- data/lib/active_project/resource_factory.rb +18 -0
- data/lib/active_project/resources/base_resource.rb +13 -14
- data/lib/active_project/resources/comment.rb +46 -2
- data/lib/active_project/resources/issue.rb +106 -18
- data/lib/active_project/resources/persistable_resource.rb +47 -0
- data/lib/active_project/resources/project.rb +1 -1
- data/lib/active_project/status_mapper.rb +145 -0
- data/lib/active_project/version.rb +1 -1
- data/lib/active_project/webhook_event.rb +34 -12
- data/lib/activeproject.rb +11 -6
- metadata +107 -6
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveProject
|
|
4
|
+
module Adapters
|
|
5
|
+
module GithubRepo
|
|
6
|
+
module Connection
|
|
7
|
+
BASE_URL = "https://api.github.com"
|
|
8
|
+
|
|
9
|
+
# Initializes the GitHub Repo Adapter.
|
|
10
|
+
# @param config [Configurations::BaseAdapterConfiguration, Configurations::GithubConfiguration]
|
|
11
|
+
# The configuration object for GitHub.
|
|
12
|
+
# @raise [ArgumentError] if required configuration options (:owner, :repo, :access_token) are missing.
|
|
13
|
+
def initialize(config:)
|
|
14
|
+
unless config.is_a?(ActiveProject::Configurations::BaseAdapterConfiguration)
|
|
15
|
+
raise ArgumentError, "GithubRepoAdapter requires a BaseAdapterConfiguration object"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@config = config
|
|
19
|
+
|
|
20
|
+
# Extract required configuration parameters
|
|
21
|
+
owner = @config.options[:owner]
|
|
22
|
+
repo = @config.options[:repo]
|
|
23
|
+
access_token = @config.options[:access_token]
|
|
24
|
+
|
|
25
|
+
# Validate required configuration parameters
|
|
26
|
+
unless owner && !owner.empty?
|
|
27
|
+
raise ArgumentError, "GithubRepoAdapter configuration requires :owner"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
unless repo && !repo.empty?
|
|
31
|
+
raise ArgumentError, "GithubRepoAdapter configuration requires :repo"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless access_token && !access_token.empty?
|
|
35
|
+
raise ArgumentError, "GithubRepoAdapter configuration requires :access_token"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Set repository path for API requests
|
|
39
|
+
@repo_path = "repos/#{owner}/#{repo}"
|
|
40
|
+
@connection = initialize_connection
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Initializes the Faraday connection object.
|
|
46
|
+
# @return [Faraday::Connection] Configured Faraday connection for GitHub API
|
|
47
|
+
def initialize_connection
|
|
48
|
+
access_token = @config.options[:access_token]
|
|
49
|
+
|
|
50
|
+
Faraday.new(url: BASE_URL) do |conn|
|
|
51
|
+
conn.request :authorization, :bearer, access_token
|
|
52
|
+
conn.request :retry
|
|
53
|
+
conn.headers["Accept"] = "application/vnd.github.v3+json"
|
|
54
|
+
conn.headers["Content-Type"] = "application/json"
|
|
55
|
+
conn.headers["User-Agent"] = ActiveProject.user_agent
|
|
56
|
+
conn.response :raise_error
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveProject
|
|
4
|
+
module Adapters
|
|
5
|
+
module GithubRepo
|
|
6
|
+
module Issues
|
|
7
|
+
# Lists GitHub issues within a specific repository (project).
|
|
8
|
+
# @param project_id [String] The repository name or full_name
|
|
9
|
+
# @param options [Hash] Optional filtering options.
|
|
10
|
+
# Supported keys:
|
|
11
|
+
# - status: 'open', 'closed', or 'all' (default: 'open')
|
|
12
|
+
# - page: Page number for pagination (default: 1)
|
|
13
|
+
# - per_page: Issues per page (default: 30, max: 100)
|
|
14
|
+
# - sort: 'created', 'updated', or 'comments' (default: 'created')
|
|
15
|
+
# - direction: 'asc' or 'desc' (default: 'desc')
|
|
16
|
+
# @return [Array<ActiveProject::Resources::Issue>]
|
|
17
|
+
def list_issues(project_id, options = {})
|
|
18
|
+
# Determine the repository path to use
|
|
19
|
+
repo_path = determine_repo_path(project_id)
|
|
20
|
+
|
|
21
|
+
# Build query parameters
|
|
22
|
+
query = {}
|
|
23
|
+
query[:state] = options[:status] || "open"
|
|
24
|
+
query[:page] = options[:page] if options[:page]
|
|
25
|
+
query[:per_page] = options[:per_page] if options[:per_page]
|
|
26
|
+
query[:sort] = options[:sort] if options[:sort]
|
|
27
|
+
query[:direction] = options[:direction] if options[:direction]
|
|
28
|
+
|
|
29
|
+
issues_data = make_request(:get, "#{repo_path}/issues", nil, query)
|
|
30
|
+
return [] unless issues_data.is_a?(Array)
|
|
31
|
+
|
|
32
|
+
issues_data.map { |issue_data| map_issue_data(issue_data) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Finds a specific issue by its number.
|
|
36
|
+
# @param id [String, Integer] The issue number within the repository.
|
|
37
|
+
# @param context [Hash] Optional context.
|
|
38
|
+
# Supported keys:
|
|
39
|
+
# - repo_owner: Repository owner if different from configured owner
|
|
40
|
+
# - repo_name: Repository name if different from configured repo
|
|
41
|
+
# @return [ActiveProject::Resources::Issue]
|
|
42
|
+
def find_issue(id, context = {})
|
|
43
|
+
# Determine the repository path to use
|
|
44
|
+
repo_path = if context[:repo_owner] && context[:repo_name]
|
|
45
|
+
"repos/#{context[:repo_owner]}/#{context[:repo_name]}"
|
|
46
|
+
else
|
|
47
|
+
@repo_path
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
issue_data = make_request(:get, "#{repo_path}/issues/#{id}")
|
|
51
|
+
map_issue_data(issue_data)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Creates a new issue in a GitHub repository.
|
|
55
|
+
# @param project_id [String] The repository name or full_name
|
|
56
|
+
# @param attributes [Hash] Issue attributes.
|
|
57
|
+
# Required: :title
|
|
58
|
+
# Optional: :description (body), :assignees (array of usernames)
|
|
59
|
+
# @return [ActiveProject::Resources::Issue]
|
|
60
|
+
def create_issue(project_id, attributes)
|
|
61
|
+
# Determine the repository path to use
|
|
62
|
+
repo_path = determine_repo_path(project_id)
|
|
63
|
+
|
|
64
|
+
unless attributes[:title] && !attributes[:title].empty?
|
|
65
|
+
raise ArgumentError, "Missing required attribute for GitHub issue creation: :title"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
data = {
|
|
69
|
+
title: attributes[:title],
|
|
70
|
+
body: attributes[:description]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Convert assignees if present
|
|
74
|
+
if attributes[:assignees] && attributes[:assignees].is_a?(Array)
|
|
75
|
+
if attributes[:assignees].all? { |a| a.is_a?(Hash) && a[:name] }
|
|
76
|
+
data[:assignees] = attributes[:assignees].map { |a| a[:name] }
|
|
77
|
+
else
|
|
78
|
+
data[:assignees] = attributes[:assignees]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Add labels if present
|
|
83
|
+
data[:labels] = attributes[:labels] if attributes[:labels]
|
|
84
|
+
|
|
85
|
+
issue_data = make_request(:post, "#{repo_path}/issues", data)
|
|
86
|
+
map_issue_data(issue_data)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Updates an existing issue in GitHub.
|
|
90
|
+
# @param id [String, Integer] The issue number.
|
|
91
|
+
# @param attributes [Hash] Issue attributes to update.
|
|
92
|
+
# Supported keys: :title, :description (body), :status (state), :assignees
|
|
93
|
+
# @param context [Hash] Optional context.
|
|
94
|
+
# Supported keys:
|
|
95
|
+
# - repo_owner: Repository owner if different from configured owner
|
|
96
|
+
# - repo_name: Repository name if different from configured repo
|
|
97
|
+
# @return [ActiveProject::Resources::Issue]
|
|
98
|
+
def update_issue(id, attributes, context = {})
|
|
99
|
+
# Determine the repository path to use
|
|
100
|
+
repo_path = if context[:repo_owner] && context[:repo_name]
|
|
101
|
+
"repos/#{context[:repo_owner]}/#{context[:repo_name]}"
|
|
102
|
+
else
|
|
103
|
+
@repo_path
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
data = {}
|
|
107
|
+
data[:title] = attributes[:title] if attributes.key?(:title)
|
|
108
|
+
data[:body] = attributes[:description] if attributes.key?(:description)
|
|
109
|
+
|
|
110
|
+
# Handle status mapping
|
|
111
|
+
if attributes.key?(:status)
|
|
112
|
+
state = case attributes[:status]
|
|
113
|
+
when :open, :in_progress then "open"
|
|
114
|
+
when :closed then "closed"
|
|
115
|
+
else attributes[:status].to_s
|
|
116
|
+
end
|
|
117
|
+
data[:state] = state
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Convert assignees if present
|
|
121
|
+
if attributes.key?(:assignees)
|
|
122
|
+
if attributes[:assignees].nil? || attributes[:assignees].empty?
|
|
123
|
+
data[:assignees] = []
|
|
124
|
+
elsif attributes[:assignees].all? { |a| a.is_a?(Hash) && a[:name] }
|
|
125
|
+
data[:assignees] = attributes[:assignees].map { |a| a[:name] }
|
|
126
|
+
else
|
|
127
|
+
data[:assignees] = attributes[:assignees]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
issue_data = make_request(:patch, "#{repo_path}/issues/#{id}", data)
|
|
132
|
+
map_issue_data(issue_data)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Attempts to delete an issue in GitHub, but since GitHub doesn't support
|
|
136
|
+
# true deletion, it closes the issue instead.
|
|
137
|
+
# @param id [String, Integer] The issue number.
|
|
138
|
+
# @param context [Hash] Optional context.
|
|
139
|
+
# @return [Boolean] Always returns false since GitHub doesn't support true deletion.
|
|
140
|
+
def delete_issue(id, context = {})
|
|
141
|
+
# GitHub doesn't support true deletion of issues
|
|
142
|
+
# The best we can do is close the issue
|
|
143
|
+
update_issue(id, { status: :closed }, context)
|
|
144
|
+
false # Return false indicating true deletion is not supported
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
# Determines the repository path to use based on project_id.
|
|
150
|
+
# @param project_id [String] Repository name or full_name
|
|
151
|
+
# @return [String] The repository API path
|
|
152
|
+
def determine_repo_path(project_id)
|
|
153
|
+
# If project_id matches configured repo or is the same as the full_name, use @repo_path
|
|
154
|
+
if project_id.to_s == @config.options[:repo] ||
|
|
155
|
+
project_id.to_s == "#{@config.options[:owner]}/#{@config.options[:repo]}"
|
|
156
|
+
return @repo_path
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# If project_id contains a slash, assume it's a full_name
|
|
160
|
+
if project_id.to_s.include?("/")
|
|
161
|
+
return "repos/#{project_id}"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Otherwise, assume it's just a repo name and use the configured owner
|
|
165
|
+
"repos/#{@config.options[:owner]}/#{project_id}"
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Maps raw GitHub issue data to an ActiveProject::Resources::Issue
|
|
169
|
+
# @param issue_data [Hash] Raw issue data from GitHub API
|
|
170
|
+
# @return [ActiveProject::Resources::Issue]
|
|
171
|
+
def map_issue_data(issue_data)
|
|
172
|
+
# Map state to status
|
|
173
|
+
status = @config.status_mappings[issue_data["state"]] || :unknown
|
|
174
|
+
|
|
175
|
+
# Map assignees
|
|
176
|
+
assignees = []
|
|
177
|
+
if issue_data["assignees"] && !issue_data["assignees"].empty?
|
|
178
|
+
assignees = issue_data["assignees"].map do |assignee|
|
|
179
|
+
Resources::User.new(
|
|
180
|
+
self,
|
|
181
|
+
id: assignee["id"].to_s,
|
|
182
|
+
name: assignee["login"],
|
|
183
|
+
adapter_source: :github,
|
|
184
|
+
raw_data: assignee
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Map reporter (user who created the issue)
|
|
190
|
+
reporter = nil
|
|
191
|
+
if issue_data["user"]
|
|
192
|
+
reporter = Resources::User.new(
|
|
193
|
+
self,
|
|
194
|
+
id: issue_data["user"]["id"].to_s,
|
|
195
|
+
name: issue_data["user"]["login"],
|
|
196
|
+
adapter_source: :github,
|
|
197
|
+
raw_data: issue_data["user"]
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Extract project ID (repo name) from the URL
|
|
202
|
+
project_id = nil
|
|
203
|
+
if issue_data["repository_url"]
|
|
204
|
+
# Extract owner/repo from repository_url
|
|
205
|
+
repo_parts = issue_data["repository_url"].split("/")
|
|
206
|
+
project_id = repo_parts.last(2).join("/")
|
|
207
|
+
elsif issue_data["url"]
|
|
208
|
+
# Try to extract from issue URL
|
|
209
|
+
url_parts = issue_data["url"].split("/")
|
|
210
|
+
if url_parts.include?("repos")
|
|
211
|
+
repos_index = url_parts.index("repos")
|
|
212
|
+
if repos_index && repos_index + 2 < url_parts.length
|
|
213
|
+
project_id = "#{url_parts[repos_index + 1]}/#{url_parts[repos_index + 2]}"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# If still not found, use configured repo
|
|
219
|
+
project_id ||= "#{@config.options[:owner]}/#{@config.options[:repo]}"
|
|
220
|
+
|
|
221
|
+
Resources::Issue.new(
|
|
222
|
+
self,
|
|
223
|
+
id: issue_data["id"].to_s,
|
|
224
|
+
key: issue_data["number"].to_s,
|
|
225
|
+
title: issue_data["title"],
|
|
226
|
+
description: issue_data["body"],
|
|
227
|
+
status: status,
|
|
228
|
+
assignees: assignees,
|
|
229
|
+
reporter: reporter,
|
|
230
|
+
project_id: project_id,
|
|
231
|
+
created_at: issue_data["created_at"] ? Time.parse(issue_data["created_at"]) : nil,
|
|
232
|
+
updated_at: issue_data["updated_at"] ? Time.parse(issue_data["updated_at"]) : nil,
|
|
233
|
+
due_on: nil, # GitHub issues don't have a built-in due date
|
|
234
|
+
priority: nil, # GitHub issues don't have a built-in priority
|
|
235
|
+
adapter_source: :github,
|
|
236
|
+
raw_data: issue_data
|
|
237
|
+
)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveProject
|
|
4
|
+
module Adapters
|
|
5
|
+
module GithubRepo
|
|
6
|
+
module Projects
|
|
7
|
+
# Lists projects accessible by the configured credentials.
|
|
8
|
+
# In GitHub's context, this returns the configured repository as a "project".
|
|
9
|
+
# @return [Array<ActiveProject::Resources::Project>] Array containing the repository as a project
|
|
10
|
+
def list_projects
|
|
11
|
+
# Get the configured repository
|
|
12
|
+
repo_data = make_request(:get, @repo_path)
|
|
13
|
+
[ map_repository_to_project(repo_data) ]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Finds a specific project by its ID or name.
|
|
17
|
+
# In GitHub's context, this finds a repository.
|
|
18
|
+
# @param id [String, Integer] The ID or full_name of the repository
|
|
19
|
+
# @return [ActiveProject::Resources::Project, nil] The repository as a project
|
|
20
|
+
def find_project(id)
|
|
21
|
+
# If id is nil or empty, use the configured repo
|
|
22
|
+
if id.nil? || id.to_s.empty?
|
|
23
|
+
id = @config.options[:repo]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# If id matches our configured repo, return that
|
|
27
|
+
if id.to_s == @config.options[:repo] || id.to_s == "#{@config.options[:owner]}/#{@config.options[:repo]}"
|
|
28
|
+
repo_data = make_request(:get, @repo_path)
|
|
29
|
+
return map_repository_to_project(repo_data)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Otherwise, try to find by ID or full name
|
|
33
|
+
begin
|
|
34
|
+
repo_data = make_request(:get, "repositories/#{id}")
|
|
35
|
+
map_repository_to_project(repo_data)
|
|
36
|
+
rescue NotFoundError
|
|
37
|
+
# Try with full name path format (owner/repo)
|
|
38
|
+
if id.to_s.include?("/")
|
|
39
|
+
repo_data = make_request(:get, "repos/#{id}")
|
|
40
|
+
map_repository_to_project(repo_data)
|
|
41
|
+
else
|
|
42
|
+
# Try with owner + repo name
|
|
43
|
+
begin
|
|
44
|
+
repo_data = make_request(:get, "repos/#{@config.options[:owner]}/#{id}")
|
|
45
|
+
map_repository_to_project(repo_data)
|
|
46
|
+
rescue NotFoundError
|
|
47
|
+
raise NotFoundError, "GitHub repository with ID or name '#{id}' not found"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Creates a new repository (project).
|
|
54
|
+
# Note: In most cases users will already have repositories set up.
|
|
55
|
+
# @param attributes [Hash] Repository attributes (name, description, etc.)
|
|
56
|
+
# @return [ActiveProject::Resources::Project] The created repository as a project
|
|
57
|
+
def create_project(attributes)
|
|
58
|
+
# Create in organization or user account based on config
|
|
59
|
+
owner = @config.options[:owner]
|
|
60
|
+
|
|
61
|
+
# Determine if creating in org or personal account
|
|
62
|
+
begin
|
|
63
|
+
make_request(:get, "orgs/#{owner}")
|
|
64
|
+
path = "orgs/#{owner}/repos"
|
|
65
|
+
rescue NotFoundError
|
|
66
|
+
path = "user/repos"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
data = {
|
|
70
|
+
name: attributes[:name],
|
|
71
|
+
description: attributes[:description],
|
|
72
|
+
private: attributes[:private] || false,
|
|
73
|
+
has_issues: attributes[:has_issues] || true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
repo_data = make_request(:post, path, data)
|
|
77
|
+
map_repository_to_project(repo_data)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Deletes a repository.
|
|
81
|
+
# Note: This is a destructive operation and generally not recommended.
|
|
82
|
+
# @param repo_id [String] The ID or full_name of the repository
|
|
83
|
+
# @return [Boolean] True if successfully deleted
|
|
84
|
+
def delete_project(repo_id)
|
|
85
|
+
# Find the repository first to get its full path
|
|
86
|
+
repo = find_project(repo_id)
|
|
87
|
+
raise NotFoundError, "Repository not found" unless repo
|
|
88
|
+
|
|
89
|
+
# Delete requires the full path in "owner/repo" format
|
|
90
|
+
full_path = repo.name # We store full_name in the name field
|
|
91
|
+
make_request(:delete, "repos/#{full_path}")
|
|
92
|
+
true
|
|
93
|
+
rescue NotFoundError
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
# Maps a GitHub repository to an ActiveProject project resource.
|
|
100
|
+
# @param repo_data [Hash] Raw repository data from GitHub API
|
|
101
|
+
# @return [ActiveProject::Resources::Project] The mapped project resource
|
|
102
|
+
def map_repository_to_project(repo_data)
|
|
103
|
+
Resources::Project.new(
|
|
104
|
+
self,
|
|
105
|
+
id: repo_data["id"].to_s,
|
|
106
|
+
key: repo_data["name"], # Repository name (without owner)
|
|
107
|
+
name: repo_data["full_name"], # Full repository name (owner/repo)
|
|
108
|
+
description: repo_data["description"],
|
|
109
|
+
adapter_source: :github,
|
|
110
|
+
raw_data: repo_data
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|