rujira 0.5.1 → 0.7.1
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/CHANGELOG.md +24 -20
- data/README.md +25 -3
- data/Rakefile +8 -0
- data/compose.yaml +1 -1
- data/examples/create_issue_as_obj.rb +31 -0
- data/examples/create_issue_in_sprint.rb +8 -14
- data/lib/rujira/api/board.rb +140 -1
- data/lib/rujira/api/comment.rb +22 -0
- data/lib/rujira/api/common.rb +47 -1
- data/lib/rujira/api/issue/comments.rb +1 -22
- data/lib/rujira/client.rb +34 -9
- data/lib/rujira/request.rb +17 -0
- data/lib/rujira/resource/comment.rb +50 -0
- data/lib/rujira/resource/common.rb +11 -0
- data/lib/rujira/resource/issue.rb +56 -0
- data/lib/rujira/resource/myself.rb +42 -0
- data/lib/rujira/resource/project.rb +48 -0
- data/lib/rujira/tasks/generate.rake +17 -5
- data/lib/rujira/version.rb +1 -1
- data/lib/rujira.rb +5 -13
- metadata +7 -2
- data/lib/rujira/error.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f34a8819ae3c7e4746ba9230b4a90fc4e12bbd07511b637bc9f525e154658cb
|
|
4
|
+
data.tar.gz: 0ae20244cf2b25fff933d644499d749d62e41a69fefe71f98c884d716935e6ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a7f45cf6ca2ec3b6e812d1acc0c4286453da1b0f5695f19c4bb73e27e5b12f2ed6b3c13a0b8f3c887035c93fb4d84c4bdcb209e1374bcfd42d259d843f966d4
|
|
7
|
+
data.tar.gz: '07279fa66131a36441a959028e7d41ee6cffade569f4fe251db24dff47ba412452810f1930c321a7c999aa9dbf1f77414f31f3f345c66f4425721f5d8e64a608'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## [0.7.0] - 2025-10-01
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- Added project as obj
|
|
6
|
+
|
|
7
|
+
### 🚜 Refactor
|
|
8
|
+
|
|
9
|
+
- Tuning for tests
|
|
10
|
+
|
|
11
|
+
### 📚 Documentation
|
|
12
|
+
|
|
13
|
+
- Updated commentaries for resources
|
|
14
|
+
## [0.6.0] - 2025-09-17
|
|
15
|
+
|
|
16
|
+
### 🚀 Features
|
|
17
|
+
|
|
18
|
+
- Added addapter method
|
|
19
|
+
- Commitable style
|
|
20
|
+
- Response object added
|
|
21
|
+
|
|
22
|
+
### 🚜 Refactor
|
|
23
|
+
|
|
24
|
+
- Changes for a client
|
|
1
25
|
## [0.5.1] - 2025-09-15
|
|
2
26
|
|
|
3
27
|
### 🚜 Refactor
|
|
@@ -62,23 +86,3 @@
|
|
|
62
86
|
- Cleanup data method
|
|
63
87
|
- Abort if id is nil #2
|
|
64
88
|
- Abort if id is nil
|
|
65
|
-
|
|
66
|
-
### 📚 Documentation
|
|
67
|
-
|
|
68
|
-
- Use ENV for URL getting
|
|
69
|
-
- Added some docs
|
|
70
|
-
## [0.3.0] - 2025-09-13
|
|
71
|
-
|
|
72
|
-
### 🚀 Features
|
|
73
|
-
|
|
74
|
-
- Added client supports
|
|
75
|
-
|
|
76
|
-
### 🚜 Refactor
|
|
77
|
-
|
|
78
|
-
- Updates tasks
|
|
79
|
-
- Use params
|
|
80
|
-
- Added examples
|
|
81
|
-
|
|
82
|
-
### Reafactor
|
|
83
|
-
|
|
84
|
-
- How to search sprints
|
data/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# RUJIRA
|
|
2
2
|
|
|
3
|
+
[](https://github.com/itmagelab/rujira/actions/workflows/ruby.yml)
|
|
4
|
+
|
|
3
5
|
**RUJIRA** is a Ruby gem for easy interaction with the Jira API. It provides a simple and flexible interface to work with Jira resources and includes Rake tasks for convenient command-line operations.
|
|
4
6
|
|
|
7
|
+
This project was created as an alternative to <https://github.com/sumoheavy/jira-ruby>, offering a more user-friendly and intuitive interface. It lets you work with requests as objects or hash arrays, provides flexibility in choosing a connection adapter, and makes the codebase easier to maintain.
|
|
8
|
+
|
|
5
9
|
---
|
|
6
10
|
|
|
7
11
|
## Features
|
|
@@ -16,7 +20,7 @@
|
|
|
16
20
|
Add to your `Gemfile`:
|
|
17
21
|
|
|
18
22
|
```ruby
|
|
19
|
-
gem 'rujira', '~> 0.
|
|
23
|
+
gem 'rujira', '~> 0.7.0'
|
|
20
24
|
```
|
|
21
25
|
|
|
22
26
|
Or install directly:
|
|
@@ -33,13 +37,31 @@ gem install rujira
|
|
|
33
37
|
|
|
34
38
|
```ruby
|
|
35
39
|
❯ cat .env
|
|
36
|
-
RUJIRA_DEBUG=false
|
|
37
40
|
RUJIRA_TOKEN='<TOKEN>'
|
|
38
|
-
|
|
41
|
+
RUJIRA_DEBUG=true
|
|
42
|
+
LOG_LEVEL=error
|
|
39
43
|
```
|
|
40
44
|
|
|
41
45
|
### Example of usage
|
|
42
46
|
|
|
47
|
+
By default, we can use an object-oriented approach, but this method does not cover all API capabilities.
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
|
|
51
|
+
client = Rujira::Client.new(url, dispatchable: false)
|
|
52
|
+
|
|
53
|
+
project = random_name
|
|
54
|
+
me = client.Myself.get
|
|
55
|
+
project = me.create_software_project key: project.to_s,
|
|
56
|
+
name: project.to_s
|
|
57
|
+
task = project.add_task summary: 'BOT: added a new task.',
|
|
58
|
+
description: 'This task was generated by the bot when creating changes in the repository.'
|
|
59
|
+
task.add_comment 'Bot added a comment as obj #1'
|
|
60
|
+
task.attach_file '/tmp/upload.file'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Alternatively, we can work with the API directly, as with a regular request dispatcher; in this form, most operations are available.
|
|
64
|
+
|
|
43
65
|
```ruby
|
|
44
66
|
require 'date'
|
|
45
67
|
|
data/Rakefile
CHANGED
|
@@ -23,3 +23,11 @@ end
|
|
|
23
23
|
task :version do
|
|
24
24
|
puts Rujira::VERSION
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
desc 'Cleanup test installation'
|
|
28
|
+
task :cleanup do
|
|
29
|
+
url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
|
|
30
|
+
client = Rujira::Client.new(url, dispatchable: false)
|
|
31
|
+
projects = client.Project.list
|
|
32
|
+
projects.map(&:delete)
|
|
33
|
+
end
|
data/compose.yaml
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dotenv'
|
|
4
|
+
require_relative '../lib/rujira'
|
|
5
|
+
|
|
6
|
+
Dotenv.load
|
|
7
|
+
|
|
8
|
+
client = Rujira::Client.new('http://localhost:8080', dispatchable: false)
|
|
9
|
+
|
|
10
|
+
project_name = 'TEST124'
|
|
11
|
+
project_key = project_name
|
|
12
|
+
|
|
13
|
+
issue = client.Issue.create do
|
|
14
|
+
payload fields: {
|
|
15
|
+
project: { key: project_key },
|
|
16
|
+
summary: 'BOT: added a new feature.',
|
|
17
|
+
description: 'This task was generated by the bot when creating changes in the repository.',
|
|
18
|
+
issuetype: { name: 'Task' }
|
|
19
|
+
}
|
|
20
|
+
params updateHistory: true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
puts "Issue create #{issue.key}"
|
|
24
|
+
|
|
25
|
+
issue.add_comment('Bot added a comment as obj #1')
|
|
26
|
+
comment = issue.add_comment('Bot added a comment as obj #2')
|
|
27
|
+
comment.delete
|
|
28
|
+
|
|
29
|
+
issue.add_comment('Bot added a comment as obj #3')
|
|
30
|
+
|
|
31
|
+
issue.delete
|
|
@@ -7,21 +7,15 @@ Dotenv.load
|
|
|
7
7
|
|
|
8
8
|
client = Rujira::Client.new('http://localhost:8080', dispatchable: true)
|
|
9
9
|
|
|
10
|
-
project_name = '
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
project_name = 'TEST124'
|
|
11
|
+
project_key = project_name
|
|
13
12
|
name = client.Myself.get['name']
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
lead: name
|
|
21
|
-
end
|
|
22
|
-
rescue StandardError
|
|
23
|
-
projects = client.Project.list
|
|
24
|
-
project = projects.find { |p| p['name'] == project_name }
|
|
14
|
+
project = client.Project.create do
|
|
15
|
+
payload key: project_key,
|
|
16
|
+
name: project_name.to_s,
|
|
17
|
+
projectTypeKey: 'software',
|
|
18
|
+
lead: name
|
|
25
19
|
end
|
|
26
20
|
|
|
27
21
|
client.Issue.create do
|
|
@@ -43,7 +37,7 @@ sprint = client.Sprint.create do
|
|
|
43
37
|
goal: 'Finish core features for release 1.0',
|
|
44
38
|
startDate: now,
|
|
45
39
|
endDate: before,
|
|
46
|
-
autoStartStop:
|
|
40
|
+
autoStartStop: false
|
|
47
41
|
end
|
|
48
42
|
|
|
49
43
|
client.Sprint.update sprint['id'] do
|
data/lib/rujira/api/board.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Rujira
|
|
|
8
8
|
# API reference:
|
|
9
9
|
# https://docs.atlassian.com/jira-software/REST/9.17.0/#agile/1.0/board
|
|
10
10
|
#
|
|
11
|
-
class Board < Common
|
|
11
|
+
class Board < Common # rubocop:disable Metrics/ClassLength
|
|
12
12
|
# Initializes a new Board API client.
|
|
13
13
|
#
|
|
14
14
|
# @param [Object] client The HTTP client instance used to perform requests.
|
|
@@ -63,6 +63,145 @@ module Rujira
|
|
|
63
63
|
call
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def create(&block)
|
|
67
|
+
builder do
|
|
68
|
+
method :post
|
|
69
|
+
path 'board'
|
|
70
|
+
instance_eval(&block) if block_given?
|
|
71
|
+
end
|
|
72
|
+
call
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def delete(id, &block)
|
|
76
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
77
|
+
builder do
|
|
78
|
+
method :delete
|
|
79
|
+
path "board/#{id}"
|
|
80
|
+
instance_eval(&block) if block_given?
|
|
81
|
+
end
|
|
82
|
+
call
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def backlog(id, &block)
|
|
86
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
87
|
+
builder do
|
|
88
|
+
path "board/#{id}/backlog"
|
|
89
|
+
instance_eval(&block) if block_given?
|
|
90
|
+
end
|
|
91
|
+
call
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def configuration(id, &block)
|
|
95
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
96
|
+
builder do
|
|
97
|
+
path "board/#{id}/configuration"
|
|
98
|
+
instance_eval(&block) if block_given?
|
|
99
|
+
end
|
|
100
|
+
call
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def issue(id, &block)
|
|
104
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
105
|
+
builder do
|
|
106
|
+
path "board/#{id}/issue"
|
|
107
|
+
instance_eval(&block) if block_given?
|
|
108
|
+
end
|
|
109
|
+
call
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def epic(id, &block)
|
|
113
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
114
|
+
builder do
|
|
115
|
+
path "board/#{id}/epic"
|
|
116
|
+
instance_eval(&block) if block_given?
|
|
117
|
+
end
|
|
118
|
+
call
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def epic_issues(id, epic_id, &block)
|
|
122
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
123
|
+
builder do
|
|
124
|
+
path "board/#{id}/epic/#{epic_id}/issue"
|
|
125
|
+
instance_eval(&block) if block_given?
|
|
126
|
+
end
|
|
127
|
+
call
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def epic_none_issues(id, &block)
|
|
131
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
132
|
+
builder do
|
|
133
|
+
path "board/#{id}/epic/none/issue"
|
|
134
|
+
instance_eval(&block) if block_given?
|
|
135
|
+
end
|
|
136
|
+
call
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def project(id, &block)
|
|
140
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
141
|
+
builder do
|
|
142
|
+
path "board/#{id}/project"
|
|
143
|
+
instance_eval(&block) if block_given?
|
|
144
|
+
end
|
|
145
|
+
call
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def settings(id, &block)
|
|
149
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
150
|
+
builder do
|
|
151
|
+
path "board/#{id}/settings/refined-velocity"
|
|
152
|
+
instance_eval(&block) if block_given?
|
|
153
|
+
end
|
|
154
|
+
call
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def set_settings(id, &block)
|
|
158
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
159
|
+
builder do
|
|
160
|
+
method :put
|
|
161
|
+
path "board/#{id}/settings/refined-velocity"
|
|
162
|
+
instance_eval(&block) if block_given?
|
|
163
|
+
end
|
|
164
|
+
call
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def properties(id, &block)
|
|
168
|
+
abort 'Board ID is required' if id.to_s.strip.empty?
|
|
169
|
+
builder do
|
|
170
|
+
path "board/#{id}/properties"
|
|
171
|
+
instance_eval(&block) if block_given?
|
|
172
|
+
end
|
|
173
|
+
call
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def set_properties(id, property_key, &block)
|
|
177
|
+
abort 'board id is required' if id.to_s.strip.empty?
|
|
178
|
+
builder do
|
|
179
|
+
method :put
|
|
180
|
+
path "board/#{id}/properties/#{property_key}"
|
|
181
|
+
instance_eval(&block) if block_given?
|
|
182
|
+
end
|
|
183
|
+
call
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def get_properties(id, property_key, &block)
|
|
187
|
+
abort 'board id is required' if id.to_s.strip.empty?
|
|
188
|
+
builder do
|
|
189
|
+
path "board/#{id}/properties/#{property_key}"
|
|
190
|
+
instance_eval(&block) if block_given?
|
|
191
|
+
end
|
|
192
|
+
call
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def delete_properties(id, property_key, &block)
|
|
196
|
+
abort 'board id is required' if id.to_s.strip.empty?
|
|
197
|
+
builder do
|
|
198
|
+
method :delete
|
|
199
|
+
path "board/#{id}/properties/#{property_key}"
|
|
200
|
+
instance_eval(&block) if block_given?
|
|
201
|
+
end
|
|
202
|
+
call
|
|
203
|
+
end
|
|
204
|
+
|
|
66
205
|
# Retrieves all sprints for a specific board.
|
|
67
206
|
#
|
|
68
207
|
# Available query parameters:
|
data/lib/rujira/api/comment.rb
CHANGED
|
@@ -19,6 +19,8 @@ module Rujira
|
|
|
19
19
|
# end
|
|
20
20
|
#
|
|
21
21
|
def create(id_or_key, &block)
|
|
22
|
+
owned_by id_or_key
|
|
23
|
+
|
|
22
24
|
abort 'Issue ID or KEY is required' if id_or_key.to_s.strip.empty?
|
|
23
25
|
builder do
|
|
24
26
|
path "issue/#{id_or_key}/comment"
|
|
@@ -27,6 +29,26 @@ module Rujira
|
|
|
27
29
|
end
|
|
28
30
|
call
|
|
29
31
|
end
|
|
32
|
+
|
|
33
|
+
# Deletes a comment from an issue.
|
|
34
|
+
#
|
|
35
|
+
# @param [String] id_or_key The issue ID or key.
|
|
36
|
+
# @param [String] id The comment ID.
|
|
37
|
+
# @yield [builder] Optional block to configure additional request parameters.
|
|
38
|
+
# @return [Object] The API response after deleting the comment.
|
|
39
|
+
#
|
|
40
|
+
# @example Delete a comment
|
|
41
|
+
# client.Issue.delete_comment("TEST-123", "10001")
|
|
42
|
+
#
|
|
43
|
+
def delete(id_or_key, id, &block)
|
|
44
|
+
abort 'Issue ID or KEY is required' if id_or_key.to_s.strip.empty?
|
|
45
|
+
builder do
|
|
46
|
+
path "issue/#{id_or_key}/comment/#{id}"
|
|
47
|
+
method :delete
|
|
48
|
+
instance_eval(&block) if block_given?
|
|
49
|
+
end
|
|
50
|
+
call
|
|
51
|
+
end
|
|
30
52
|
end
|
|
31
53
|
end
|
|
32
54
|
end
|
data/lib/rujira/api/common.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Rujira
|
|
|
13
13
|
def initialize(client)
|
|
14
14
|
# Store the passed client object in an instance variable for later use
|
|
15
15
|
@client = client
|
|
16
|
+
@metadata ||= {}
|
|
16
17
|
@request = Request.new.builder do
|
|
17
18
|
# Set the Bearer token for authorization
|
|
18
19
|
bearer @token
|
|
@@ -40,13 +41,58 @@ module Rujira
|
|
|
40
41
|
def call
|
|
41
42
|
@client.logger.debug "Call the method: #{caller_locations(1, 1)[0].label}"
|
|
42
43
|
return @client.dispatch(@request) if @client.dispatchable
|
|
44
|
+
return self if @client.lazy
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
to_obj
|
|
45
47
|
end
|
|
46
48
|
|
|
49
|
+
# Executes the configured request via the client's dispatch mechanism.
|
|
50
|
+
#
|
|
51
|
+
# @return [Object] The raw API response from the dispatched request.
|
|
47
52
|
def commit
|
|
48
53
|
@client.dispatch(@request)
|
|
49
54
|
end
|
|
55
|
+
alias execute commit
|
|
56
|
+
|
|
57
|
+
# Converts the API response into structured Ruby objects.
|
|
58
|
+
# If the response is an array of hashes, maps each element through `process`.
|
|
59
|
+
# If it's a single hash, processes it directly. Otherwise, returns as-is.
|
|
60
|
+
#
|
|
61
|
+
# @return [Object] Processed response as one or more resource objects, or original response.
|
|
62
|
+
def to_obj
|
|
63
|
+
response = execute
|
|
64
|
+
|
|
65
|
+
return response.map { |el| process(el) } if response.is_a?(Array) && response.all?(Hash)
|
|
66
|
+
return response unless response.is_a?(Hash)
|
|
67
|
+
|
|
68
|
+
process response
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Processes a single response hash by merging metadata and instantiating
|
|
72
|
+
# the corresponding Resource object based on the current class name.
|
|
73
|
+
#
|
|
74
|
+
# @param [Hash] response The API response hash to process.
|
|
75
|
+
# @return [Object] An instance of the corresponding Resource class.
|
|
76
|
+
# @raise [NameError] If the corresponding Resource class does not exist.
|
|
77
|
+
def process(response)
|
|
78
|
+
response.merge!(@metadata)
|
|
79
|
+
resource_class_name = self.class.name.sub('Api', 'Resource')
|
|
80
|
+
begin
|
|
81
|
+
Object.const_get(resource_class_name).new(@client, **response)
|
|
82
|
+
rescue NameError
|
|
83
|
+
raise "Resource class '#{resource_class_name}' not found. " \
|
|
84
|
+
'Please ensure the class exists or use dispatchable mode.'
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Sets ownership context for the resource by storing a parent ID or key
|
|
89
|
+
# in metadata, which may be used during object instantiation.
|
|
90
|
+
#
|
|
91
|
+
# @param [String, Integer] id_or_key The ID or key of the parent resource.
|
|
92
|
+
# @return [Hash] The updated metadata hash with the parent set.
|
|
93
|
+
def owned_by(id_or_key)
|
|
94
|
+
@metadata.merge!({ parent: id_or_key })
|
|
95
|
+
end
|
|
50
96
|
end
|
|
51
97
|
end
|
|
52
98
|
end
|
|
@@ -42,27 +42,6 @@ module Rujira
|
|
|
42
42
|
call
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# Adds a new comment to an issue.
|
|
46
|
-
#
|
|
47
|
-
# @param [String] id_or_key The issue ID or key.
|
|
48
|
-
# @yield [builder] Block to configure the payload for the new comment.
|
|
49
|
-
# @return [Object] The API response containing the created comment.
|
|
50
|
-
#
|
|
51
|
-
# @example Add a comment
|
|
52
|
-
# client.Issue.add_comment("TEST-123") do
|
|
53
|
-
# payload body: "New comment text"
|
|
54
|
-
# end
|
|
55
|
-
#
|
|
56
|
-
def add_comment(id_or_key, &block)
|
|
57
|
-
abort 'Issue ID or KEY is required' if id_or_key.to_s.strip.empty?
|
|
58
|
-
builder do
|
|
59
|
-
path "issue/#{id_or_key}/comment"
|
|
60
|
-
method :post
|
|
61
|
-
instance_eval(&block) if block_given?
|
|
62
|
-
end
|
|
63
|
-
call
|
|
64
|
-
end
|
|
65
|
-
|
|
66
45
|
# Retrieves a specific comment by its ID.
|
|
67
46
|
#
|
|
68
47
|
# @param [String] id_or_key The issue ID or key.
|
|
@@ -173,7 +152,7 @@ module Rujira
|
|
|
173
152
|
# payload body: "This is a comment"
|
|
174
153
|
# end
|
|
175
154
|
#
|
|
176
|
-
def
|
|
155
|
+
def add_comment(id_or_key, &block)
|
|
177
156
|
abort 'Issue ID or KEY is required' if id_or_key.to_s.strip.empty?
|
|
178
157
|
@client.Comment.create id_or_key, &block
|
|
179
158
|
end
|
data/lib/rujira/client.rb
CHANGED
|
@@ -11,7 +11,9 @@ module Rujira
|
|
|
11
11
|
# client.issue.get("TEST-123")
|
|
12
12
|
#
|
|
13
13
|
class Client
|
|
14
|
-
attr_reader :logger, :
|
|
14
|
+
attr_reader :dispatchable, :logger, :lazy
|
|
15
|
+
|
|
16
|
+
SUPPORTED_METHODS = %i[get delete head post put patch].freeze
|
|
15
17
|
|
|
16
18
|
# Initializes a new Jira client.
|
|
17
19
|
#
|
|
@@ -21,8 +23,9 @@ module Rujira
|
|
|
21
23
|
# @example Initialize client
|
|
22
24
|
# client = Rujira::Client.new("https://jira.example.com", debug: true)
|
|
23
25
|
#
|
|
24
|
-
def initialize(url, debug: false, dispatchable: true, log_level: 'error') # rubocop:disable Metrics/MethodLength
|
|
26
|
+
def initialize(url, debug: false, dispatchable: true, lazy: false, log_level: 'error') # rubocop:disable Metrics/MethodLength
|
|
25
27
|
@dispatchable = dispatchable
|
|
28
|
+
@lazy = lazy
|
|
26
29
|
@uri = URI(url)
|
|
27
30
|
@debug = ENV.fetch('RUJIRA_DEBUG', debug.to_s) == 'true'
|
|
28
31
|
@raise_error = false
|
|
@@ -40,6 +43,22 @@ module Rujira
|
|
|
40
43
|
ENV.fetch('LOG_LEVEL', log_level).downcase,
|
|
41
44
|
Logger::ERROR
|
|
42
45
|
)
|
|
46
|
+
|
|
47
|
+
@adapter = adapter
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def adapter # rubocop:disable Metrics/MethodLength
|
|
51
|
+
case ENV.fetch('RUJIRA_ADAPTER', nil)
|
|
52
|
+
when 'typhoeus'
|
|
53
|
+
@logger.debug 'Using Typhoeus adapter'
|
|
54
|
+
require 'faraday/typhoeus'
|
|
55
|
+
:typhoeus
|
|
56
|
+
when 'async'
|
|
57
|
+
require 'async/http/faraday'
|
|
58
|
+
:async_http
|
|
59
|
+
else
|
|
60
|
+
Faraday.default_adapter
|
|
61
|
+
end
|
|
43
62
|
end
|
|
44
63
|
|
|
45
64
|
# Dynamically instantiates the appropriate API resource class.
|
|
@@ -68,23 +87,27 @@ module Rujira
|
|
|
68
87
|
Rujira::Api.const_defined?(method_name.to_s) || super
|
|
69
88
|
end
|
|
70
89
|
|
|
90
|
+
def build_options(request)
|
|
91
|
+
{
|
|
92
|
+
url: @uri,
|
|
93
|
+
headers: request.headers,
|
|
94
|
+
params: request.params
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
71
98
|
# Executes the configured request.
|
|
72
99
|
#
|
|
73
100
|
# @return [Object] The API response body if successful
|
|
74
101
|
# @raise [RuntimeError] If the request fails or method is unsupported
|
|
75
102
|
#
|
|
76
103
|
def dispatch(request) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
77
|
-
raise "method #{request.method} not supported" unless
|
|
104
|
+
raise "method #{request.method} not supported" unless SUPPORTED_METHODS.include?(request.method)
|
|
78
105
|
|
|
79
106
|
begin
|
|
80
107
|
args = [request.path]
|
|
81
108
|
args << request.payload if %i[post put patch].include?(request.method)
|
|
82
109
|
|
|
83
|
-
options =
|
|
84
|
-
url: @uri,
|
|
85
|
-
headers: request.headers,
|
|
86
|
-
params: request.params
|
|
87
|
-
}
|
|
110
|
+
options = build_options(request)
|
|
88
111
|
response = connection(options, request.authorization).public_send(request.method, *args)
|
|
89
112
|
|
|
90
113
|
if response.success?
|
|
@@ -94,7 +117,8 @@ module Rujira
|
|
|
94
117
|
"and body #{response.body}")
|
|
95
118
|
end
|
|
96
119
|
rescue StandardError => e
|
|
97
|
-
|
|
120
|
+
@logger.error "Error: #{e.class} - #{e.message}"
|
|
121
|
+
raise
|
|
98
122
|
end
|
|
99
123
|
end
|
|
100
124
|
|
|
@@ -104,6 +128,7 @@ module Rujira
|
|
|
104
128
|
#
|
|
105
129
|
def connection(options, authorization)
|
|
106
130
|
Faraday.new(options) do |builder|
|
|
131
|
+
builder.adapter @adapter
|
|
107
132
|
builder.request :authorization, *authorization if authorization
|
|
108
133
|
builder.request :multipart, flat_encode: true
|
|
109
134
|
builder.request :json
|
data/lib/rujira/request.rb
CHANGED
|
@@ -106,6 +106,14 @@ module Rujira
|
|
|
106
106
|
@authorization = :basic, username, password
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Disables any authentication for subsequent requests.
|
|
110
|
+
#
|
|
111
|
+
# @return [void]
|
|
112
|
+
#
|
|
113
|
+
def disable_auth
|
|
114
|
+
@authorization = nil
|
|
115
|
+
end
|
|
116
|
+
|
|
109
117
|
# Gets or sets the request path (appended to rest_base_path).
|
|
110
118
|
#
|
|
111
119
|
# @param [String, nil] path The relative path to set.
|
|
@@ -128,5 +136,14 @@ module Rujira
|
|
|
128
136
|
|
|
129
137
|
@payload = payload
|
|
130
138
|
end
|
|
139
|
+
|
|
140
|
+
# Extends the current payload by merging the given hash into it.
|
|
141
|
+
#
|
|
142
|
+
# @param [Hash, nil] payload Optional hash to merge into the current payload.
|
|
143
|
+
def extend_payload(payload = nil)
|
|
144
|
+
return @payload if payload.nil?
|
|
145
|
+
|
|
146
|
+
@payload.merge!(payload)
|
|
147
|
+
end
|
|
131
148
|
end
|
|
132
149
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rujira
|
|
4
|
+
module Resource
|
|
5
|
+
# Represents a Jira comment resource.
|
|
6
|
+
# Provides access to comment attributes and actions such as deletion.
|
|
7
|
+
#
|
|
8
|
+
# Example usage:
|
|
9
|
+
# comment = Rujira::Resource::Comment.new(client, data)
|
|
10
|
+
# comment.delete # Deletes the comment via the API
|
|
11
|
+
#
|
|
12
|
+
class Comment < Common
|
|
13
|
+
# @return [String] The ID of the comment
|
|
14
|
+
attr_reader :id
|
|
15
|
+
# @return [String] The issue key or resource key associated with the comment
|
|
16
|
+
attr_reader :key
|
|
17
|
+
# @return [String] The URL to access this comment via Jira REST API
|
|
18
|
+
attr_reader :url
|
|
19
|
+
|
|
20
|
+
# Initializes a Comment resource
|
|
21
|
+
#
|
|
22
|
+
# @param [Object] client The API client instance used to make requests
|
|
23
|
+
# @param [Hash] hash The comment data from the Jira API
|
|
24
|
+
def initialize(client, **args)
|
|
25
|
+
super
|
|
26
|
+
|
|
27
|
+
@url = args['self']
|
|
28
|
+
@id = args['id']
|
|
29
|
+
|
|
30
|
+
@author = args['author']
|
|
31
|
+
@body = args['body']
|
|
32
|
+
@update_author = args['updateAuthor']
|
|
33
|
+
@created = args['created']
|
|
34
|
+
@updated = args['updated']
|
|
35
|
+
|
|
36
|
+
@parent = args[:parent] # The parent issue key or resource
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Deletes this comment from Jira.
|
|
40
|
+
#
|
|
41
|
+
# @return [Object] The API response from the delete operation
|
|
42
|
+
#
|
|
43
|
+
# @example Delete a comment
|
|
44
|
+
# comment.delete
|
|
45
|
+
def delete
|
|
46
|
+
@client.Comment.delete(@parent, @id)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rujira
|
|
4
|
+
module Resource
|
|
5
|
+
# Represents a Jira issue resource.
|
|
6
|
+
# Provides access to issue attributes and actions such as adding comments.
|
|
7
|
+
#
|
|
8
|
+
# Example usage:
|
|
9
|
+
# issue = Rujira::Resource::Issue.new(client, data)
|
|
10
|
+
# issue.add_comment("This is a comment")
|
|
11
|
+
#
|
|
12
|
+
class Issue < Common
|
|
13
|
+
# @return [String] The ID of the issue
|
|
14
|
+
attr_reader :id
|
|
15
|
+
# @return [String] The key of the issue (e.g., "TEST-123")
|
|
16
|
+
attr_reader :key
|
|
17
|
+
# @return [String] The URL to access this issue via Jira REST API
|
|
18
|
+
attr_reader :url
|
|
19
|
+
|
|
20
|
+
# Initializes an Issue resource
|
|
21
|
+
#
|
|
22
|
+
# @param [Object] client The API client instance used to make requests
|
|
23
|
+
# @param [Hash] hash The issue data from the Jira API
|
|
24
|
+
def initialize(client, **args)
|
|
25
|
+
super
|
|
26
|
+
|
|
27
|
+
@id = args['id']
|
|
28
|
+
@key = args['key']
|
|
29
|
+
@url = args['self']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Adds a comment to this issue.
|
|
33
|
+
#
|
|
34
|
+
# @param [String] text The content of the comment
|
|
35
|
+
# @return [Object] The API response after adding the comment
|
|
36
|
+
#
|
|
37
|
+
# @example Add a comment to an issue
|
|
38
|
+
# issue.add_comment("This is a new comment")
|
|
39
|
+
def add_comment(text)
|
|
40
|
+
@client.logger.debug "Adding comment to issue #{@key}"
|
|
41
|
+
@client.Issue.add_comment(@id) do
|
|
42
|
+
payload body: text
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def delete
|
|
47
|
+
@client.logger.debug "Deleting issue #{@key}"
|
|
48
|
+
@client.Issue.delete(@id)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def attach_file(path)
|
|
52
|
+
@client.Issue.attachments @key, path
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rujira
|
|
4
|
+
module Resource
|
|
5
|
+
# Represents the currently authenticated Jira user (Myself).
|
|
6
|
+
# Provides access to basic user attributes retrieved from the Jira API.
|
|
7
|
+
#
|
|
8
|
+
class Myself < Common
|
|
9
|
+
attr_reader :url, :email, :name, :id, :key
|
|
10
|
+
|
|
11
|
+
# Initializes a Myself resource with data from the Jira API response.
|
|
12
|
+
#
|
|
13
|
+
# @param [Object] client The HTTP client used for API communication.
|
|
14
|
+
# @param [Hash] args The user data hash returned by the Jira API.
|
|
15
|
+
# @option args [String] 'self' The URL of the user's Jira profile.
|
|
16
|
+
# @option args [String] 'key' The user's Jira key.
|
|
17
|
+
# @option args [String] 'name' The user's display name.
|
|
18
|
+
# @option args [String] 'emailAddress' The user's email address.
|
|
19
|
+
def initialize(client, **args)
|
|
20
|
+
super
|
|
21
|
+
|
|
22
|
+
@url = args['self']
|
|
23
|
+
|
|
24
|
+
@key = args['key']
|
|
25
|
+
@name = args['name']
|
|
26
|
+
@email = args['emailAddress']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_software_project(**args)
|
|
30
|
+
key = args[:key]
|
|
31
|
+
project_name = args[:name]
|
|
32
|
+
name = @name
|
|
33
|
+
@client.Project.create do
|
|
34
|
+
payload key: key,
|
|
35
|
+
name: project_name,
|
|
36
|
+
projectTypeKey: 'software',
|
|
37
|
+
lead: name
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rujira
|
|
4
|
+
module Resource
|
|
5
|
+
# Represents a Jira project.
|
|
6
|
+
# Provides access to project attributes and operations such as deletion.
|
|
7
|
+
#
|
|
8
|
+
class Project < Common
|
|
9
|
+
attr_reader :url, :id, :key
|
|
10
|
+
|
|
11
|
+
# Initializes a Project resource with data from the Jira API response.
|
|
12
|
+
#
|
|
13
|
+
# @param [Object] client The HTTP client used for API communication.
|
|
14
|
+
# @param [Hash] args The project data hash returned by the Jira API.
|
|
15
|
+
# @option args [String] 'self' The URL of the project in Jira.
|
|
16
|
+
# @option args [String] 'id' The internal ID of the project.
|
|
17
|
+
# @option args [String] 'key' The project key (e.g., PROJ).
|
|
18
|
+
def initialize(client, **args)
|
|
19
|
+
super
|
|
20
|
+
|
|
21
|
+
@url = args['self']
|
|
22
|
+
@id = args['id']
|
|
23
|
+
@key = args['key']
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Deletes the project from Jira using the client's Project API.
|
|
27
|
+
#
|
|
28
|
+
# @return [Object] The API response from the delete operation.
|
|
29
|
+
# @note This operation is irreversible and should be used with caution.
|
|
30
|
+
def delete
|
|
31
|
+
@client.Project.delete(@id)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def add_task(**args)
|
|
35
|
+
key = @key
|
|
36
|
+
@client.Issue.create do
|
|
37
|
+
payload fields: {
|
|
38
|
+
project: { key: key },
|
|
39
|
+
summary: args[:summary],
|
|
40
|
+
description: args[:description],
|
|
41
|
+
issuetype: { name: 'Task' }
|
|
42
|
+
}
|
|
43
|
+
params updateHistory: true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -8,7 +8,7 @@ require 'json'
|
|
|
8
8
|
module Rujira
|
|
9
9
|
module Tasks
|
|
10
10
|
# TODO
|
|
11
|
-
class Jira
|
|
11
|
+
class Jira # rubocop:disable Metrics/ClassLength
|
|
12
12
|
include Rake::DSL if defined?(Rake::DSL)
|
|
13
13
|
def initialize
|
|
14
14
|
generate
|
|
@@ -34,7 +34,7 @@ module Rujira
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def generate # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
37
|
-
namespace :jira do
|
|
37
|
+
namespace :jira do # rubocop:disable Metrics/BlockLength
|
|
38
38
|
desc 'Test connection by getting username'
|
|
39
39
|
task :whoami do
|
|
40
40
|
result = client.Myself.get
|
|
@@ -53,6 +53,18 @@ module Rujira
|
|
|
53
53
|
result = client.Project.list
|
|
54
54
|
puts JSON.pretty_generate(result)
|
|
55
55
|
end
|
|
56
|
+
|
|
57
|
+
desc 'Create project'
|
|
58
|
+
task :create do |t|
|
|
59
|
+
options = fetch_options(%w[KEY NAME TYPE LEAD], t.name)
|
|
60
|
+
result = client.Project.create do
|
|
61
|
+
payload key: options[:key],
|
|
62
|
+
name: options[:name],
|
|
63
|
+
projectTypeKey: options[:type],
|
|
64
|
+
lead: options[:lead]
|
|
65
|
+
end
|
|
66
|
+
puts JSON.pretty_generate(result)
|
|
67
|
+
end
|
|
56
68
|
end
|
|
57
69
|
|
|
58
70
|
namespace :dashboard do
|
|
@@ -107,15 +119,15 @@ module Rujira
|
|
|
107
119
|
end
|
|
108
120
|
end
|
|
109
121
|
|
|
110
|
-
namespace :issue do
|
|
122
|
+
namespace :issue do # rubocop:disable Metrics/BlockLength
|
|
111
123
|
desc 'Create a issue'
|
|
112
124
|
task :create do |t|
|
|
113
|
-
options = fetch_options(%w[
|
|
125
|
+
options = fetch_options(%w[PROJECT_KEY SUMMARY DESCRIPTION ISSUETYPE], t.name)
|
|
114
126
|
abort 'ISSUETYPE must start with a capital letter' unless options[:issuetype].match?(/\A[A-Z]/)
|
|
115
127
|
|
|
116
128
|
result = client.Issue.create do
|
|
117
129
|
payload fields: {
|
|
118
|
-
project: { key: options[:
|
|
130
|
+
project: { key: options[:project_key] },
|
|
119
131
|
summary: options[:summary],
|
|
120
132
|
issuetype: { name: options[:issuetype] },
|
|
121
133
|
description: options[:description]
|
data/lib/rujira/version.rb
CHANGED
data/lib/rujira.rb
CHANGED
|
@@ -7,17 +7,21 @@ require 'faraday'
|
|
|
7
7
|
require 'faraday/multipart'
|
|
8
8
|
require 'json'
|
|
9
9
|
|
|
10
|
-
require_relative 'rujira/error'
|
|
11
10
|
require_relative 'rujira/version'
|
|
12
11
|
require_relative 'rujira/request'
|
|
13
12
|
require_relative 'rujira/client'
|
|
14
13
|
require_relative 'rujira/api/common'
|
|
14
|
+
require_relative 'rujira/resource/common'
|
|
15
15
|
require_relative 'rujira/api/search'
|
|
16
16
|
require_relative 'rujira/api/issue'
|
|
17
|
+
require_relative 'rujira/resource/issue'
|
|
17
18
|
require_relative 'rujira/api/project'
|
|
19
|
+
require_relative 'rujira/resource/project'
|
|
18
20
|
require_relative 'rujira/api/comment'
|
|
21
|
+
require_relative 'rujira/resource/comment'
|
|
19
22
|
require_relative 'rujira/api/attachments'
|
|
20
23
|
require_relative 'rujira/api/myself'
|
|
24
|
+
require_relative 'rujira/resource/myself'
|
|
21
25
|
require_relative 'rujira/api/server_info'
|
|
22
26
|
require_relative 'rujira/api/dashboard'
|
|
23
27
|
require_relative 'rujira/api/board'
|
|
@@ -43,16 +47,4 @@ module Rujira
|
|
|
43
47
|
# raise Rujira::Error, "Something went wrong"
|
|
44
48
|
#
|
|
45
49
|
class Error < StandardError; end
|
|
46
|
-
|
|
47
|
-
# Checks if an environment variable is truthy.
|
|
48
|
-
#
|
|
49
|
-
# @param [String] var The name of the environment variable
|
|
50
|
-
# @return [Boolean] true if the value is 'true', '1', or 'yes' (case-insensitive), false otherwise
|
|
51
|
-
#
|
|
52
|
-
# @example Check if debug mode is enabled
|
|
53
|
-
# Rujira.env_var?('RUJIRA_DEBUG')
|
|
54
|
-
#
|
|
55
|
-
def self.env_var?(var)
|
|
56
|
-
%w[true 1 yes].include?(ENV[var]&.downcase)
|
|
57
|
-
end
|
|
58
50
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rujira
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Semenov
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- cliff.toml
|
|
54
54
|
- compose.yaml
|
|
55
55
|
- contrib/jira_link_generator.rb
|
|
56
|
+
- examples/create_issue_as_obj.rb
|
|
56
57
|
- examples/create_issue_in_sprint.rb
|
|
57
58
|
- lib/rujira.rb
|
|
58
59
|
- lib/rujira/api/application_properties.rb
|
|
@@ -77,8 +78,12 @@ files:
|
|
|
77
78
|
- lib/rujira/api/server_info.rb
|
|
78
79
|
- lib/rujira/api/sprint.rb
|
|
79
80
|
- lib/rujira/client.rb
|
|
80
|
-
- lib/rujira/error.rb
|
|
81
81
|
- lib/rujira/request.rb
|
|
82
|
+
- lib/rujira/resource/comment.rb
|
|
83
|
+
- lib/rujira/resource/common.rb
|
|
84
|
+
- lib/rujira/resource/issue.rb
|
|
85
|
+
- lib/rujira/resource/myself.rb
|
|
86
|
+
- lib/rujira/resource/project.rb
|
|
82
87
|
- lib/rujira/tasks/generate.rake
|
|
83
88
|
- lib/rujira/version.rb
|
|
84
89
|
- sig/rujira.rbs
|
data/lib/rujira/error.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rujira
|
|
4
|
-
class Error < StandardError; end
|
|
5
|
-
|
|
6
|
-
# TODO: add docs
|
|
7
|
-
# Some description
|
|
8
|
-
class PathArgumentError < Error
|
|
9
|
-
def message
|
|
10
|
-
"No argument to 'path' was given."
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# TODO: add docs
|
|
15
|
-
# Some description
|
|
16
|
-
class DataArgumentError < Error
|
|
17
|
-
def message
|
|
18
|
-
"No argument to 'data' was given."
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|