rujira 0.7.0 → 0.7.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e934f91388416b891e49d824c5f3d875708d5102d2084d8fa3c641be5006d02
4
- data.tar.gz: 5cc8ee2aa9475057ae26953287f5f99c9be83adc0cd578236d17b758d5c82486
3
+ metadata.gz: 22f2c365275fa72514c36702efe3fda2463bdc1948dcc020f38148b4a25bac3a
4
+ data.tar.gz: 1a4995399b9f0ff4616fbef8627c3c3b717d5e0edcc9d3f1e1defb11d9262fe8
5
5
  SHA512:
6
- metadata.gz: 83a61e445cb8b1a3e2e5f5cb338d9f31070df47c3ea1aff9be54c49a63f8d022658d76856fee5f934b0343b79d37c04ae78bf9cfd17cb721fa988f3971f9ac1f
7
- data.tar.gz: 503b7bc45991306f9346ee2b8cc1b77e492194544caea88bb3931b71d30f04a8f99fc10a76de98f6ff91d008743de259860503c61698f159b1fbc8bfc04c22de
6
+ metadata.gz: ecde3caf684b3907b6764a858ca723c09e19606fc91fa238c92c8648ba0b8827b08e7bbabbe39d221fcb6674517c9791315f88bd246145a4f02632f179deadab
7
+ data.tar.gz: a7abd29b1bdd8222ab99bf02432b4ba7fb396b8e5b3e4148bda3bd27fe70cce28f03bcef12aed0f7fb69a59bac9d6bfb4c4b7031764fb9b44359bddcd0cbadcb
data/CHANGELOG.md CHANGED
@@ -1,11 +1,43 @@
1
- ## [0.7.0] - 2025-10-01
1
+ ## [0.7.3] - 2026-03-27
2
2
 
3
3
  ### 🚀 Features
4
4
 
5
+ - Added priority API
6
+ - Added issue_type API and link generator
7
+
8
+ ### 🚜 Refactor
9
+
10
+ - Create utils libs
11
+ - Added list of issue types
12
+ - Rename dispatchable to wrap_responses
13
+ - Use editor for enter description
14
+ - Added alias for payload
15
+
16
+ ### 📚 Documentation
17
+
18
+ - Added short description for link generator
19
+ - Added example test for parallel usage
20
+ ## [0.7.2] - 2026-03-26
21
+
22
+ ### 🚀 Features
23
+
24
+ - Added user creation
25
+ ## [0.7.1] - 2026-03-25
26
+
27
+ ### 🚀 Features
28
+
29
+ - Use patch changes log
30
+ - Added exception raising for obj
31
+ - Added attach file alias
32
+ - Added api for task creation
5
33
  - Added project as obj
6
34
 
7
35
  ### 🚜 Refactor
8
36
 
37
+ - Rename commitable to lazy
38
+ - Linter issues
39
+ - Added new methods for agile boards
40
+ - Added new task
9
41
  - Tuning for tests
10
42
 
11
43
  ### 📚 Documentation
@@ -38,51 +70,3 @@
38
70
 
39
71
  - Dispatchable for client
40
72
  ## [0.4.0] - 2025-09-13
41
-
42
- ### 🚀 Features
43
-
44
- - Extend Issue #2
45
- - Extend Issue
46
- - Added Filter
47
- - Extend Avatar, Configuration, CustomFields, Field
48
- - Extend Attachments
49
- - Added ApplicationRole
50
- - Added ApplicationProperties
51
- - Added permissions api
52
-
53
- ### 🐛 Bug Fixes
54
-
55
- - Fix for tasks
56
-
57
- ### 🚜 Refactor
58
-
59
- - Dont raising for faraday
60
- - Cleanup attachments
61
- - Split issue #3
62
- - Split issue #2
63
- - Split issue
64
- ## [0.3.3] - 2025-09-13
65
-
66
- ### 📚 Documentation
67
-
68
- - Added comments from ai #4
69
- - Added comments from ai #3
70
- - Added comments from ai #2
71
- - Added comments from ai
72
- ## [0.3.2] - 2025-09-13
73
-
74
- ### 🚜 Refactor
75
-
76
- - Remove configuration
77
-
78
- ### 📚 Documentation
79
-
80
- - Updated inline description
81
- ## [0.3.1] - 2025-09-13
82
-
83
- ### 🚜 Refactor
84
-
85
- - Fix for headers setter
86
- - Cleanup data method
87
- - Abort if id is nil #2
88
- - Abort if id is nil
data/README.md CHANGED
@@ -1,7 +1,11 @@
1
1
  # RUJIRA
2
2
 
3
+ [![Ruby](https://github.com/itmagelab/rujira/actions/workflows/ruby.yml/badge.svg)](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.6.0'
23
+ gem 'rujira', '~> 0.7.0'
20
24
  ```
21
25
 
22
26
  Or install directly:
@@ -40,6 +44,102 @@ LOG_LEVEL=error
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, wrap_responses: true)
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
+ Demonstrates the full lifecycle of a Jira user: creation, retrieval, update, deactivation, and deletion using the Rujira client.
64
+
65
+ ```ruby
66
+ url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
67
+ client = Rujira::Client.new(url, wrap_responses: true)
68
+
69
+ username = random_name
70
+ me = client.Myself.get
71
+
72
+ user = client.User.create do
73
+ payload({
74
+ name: username,
75
+ password: username,
76
+ emailAddress: "#{username}@username",
77
+ displayName: "#{username} created be #{me.name}",
78
+ applicationKeys: ['jira-core']
79
+ })
80
+ end
81
+
82
+ user = client.User.get user.key
83
+
84
+ user.update do
85
+ payload emailAddress: 'root@test'
86
+ end
87
+
88
+ user.deactivate!
89
+ user.delete
90
+ ```
91
+
92
+ This function demonstrates that the library supports executing requests in parallel using multiple threads. By processing tasks concurrently, it significantly improves performance when handling a large number of repetitive operations, such as creating or deleting entities.
93
+
94
+ ```ruby
95
+ url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
96
+ client = Rujira::Client.new(url, debug: true, wrap_responses: false, lazy: true)
97
+
98
+ me = client.Myself.get.execute
99
+
100
+ queue = Queue.new
101
+
102
+ project_name = random_name
103
+ project = client.Project.create do
104
+ payload key: project_name.to_s,
105
+ name: project_name.to_s,
106
+ projectTypeKey: 'software',
107
+ lead: me['name']
108
+ end.to_obj
109
+
110
+ count.times do
111
+ task = project.add_task summary: 'BOT: added a new task.',
112
+ description: 'This task was generated by the bot when creating changes in the repository.'
113
+ queue << task
114
+ end
115
+
116
+ workers = thread.times.map do
117
+ Thread.new do
118
+ until queue.empty?
119
+ begin
120
+ task = begin
121
+ queue.pop(true)
122
+ rescue StandardError
123
+ nil
124
+ end
125
+ next unless task
126
+
127
+ task = task.to_obj
128
+
129
+ task.delete
130
+ rescue StandardError => e
131
+ warn "Error in thread: #{e.message}"
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ workers.each(&:join)
138
+ project.delete
139
+ ```
140
+
141
+ Alternatively, we can work with the API directly, as with a regular request dispatcher; in this form, most operations are available.
142
+
43
143
  ```ruby
44
144
  require 'date'
45
145
 
@@ -151,7 +251,9 @@ rake jira:dashboard:list # Get list of dashboards
151
251
  rake jira:issue:attach # Example usage attaching in issue
152
252
  rake jira:issue:create # Create a issue
153
253
  rake jira:issue:delete # Delete issue
254
+ rake jira:issue:generate_link # Generate a Jira link for creating a new issue
154
255
  rake jira:issue:search # Search issue by fields
256
+ rake jira:project:create # Create project
155
257
  rake jira:project:list # Get list of projects
156
258
  rake jira:server_info # Test connection by getting server information
157
259
  rake jira:sprint:properties:list # Get sprint properties
@@ -165,6 +267,34 @@ rake jira:issue:search JQL='project = ITMG'
165
267
  rake jira:issue:attach FILE='upload.png' ISSUE_ID='ITMG-1'
166
268
  ```
167
269
 
270
+ ### Generate a Jira Issue Creation Link
271
+
272
+ The `generate_link` task interactively collects input (project, summary, description, and issue type) and generates a ready-to-use Jira URL with pre-filled fields for creating a new issue.
273
+
274
+ It opens your default editor for entering the description and then outputs a link that can be opened in a browser.
275
+
276
+ #### Usage
277
+
278
+ ```
279
+ rake jira::issue::generate_link
280
+ ```
281
+
282
+ #### Example session
283
+
284
+ ```
285
+ Project key (e.g., ABC): TEST
286
+ Summary (short description): Fix login bug
287
+ Opening editor for description...
288
+ Issue type (Bug/Task/Story): Bug
289
+ ```
290
+
291
+ #### Output
292
+
293
+ ```
294
+ Generated issue creation link:
295
+ https://your-jira-instance/secure/CreateIssueDetails!init.jspa?pid=10000&summary=Fix+login+bug&description=Steps+to+reproduce...&reporter=john.doe&issuetype=1
296
+ ```
297
+
168
298
  ## Development runs
169
299
 
170
300
  ```bash
data/Rakefile CHANGED
@@ -23,11 +23,3 @@ 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/cliff.toml CHANGED
@@ -92,3 +92,6 @@ sort_commits = "newest"
92
92
  recurse_submodules = false
93
93
 
94
94
  limit_commits = 50
95
+
96
+ [bump]
97
+ bump_type = "patch"
data/compose.yaml CHANGED
@@ -13,7 +13,7 @@ services:
13
13
 
14
14
  db:
15
15
  image: postgres
16
- mem_limit: 128mb
16
+ mem_limit: 128mb
17
17
  restart: always
18
18
  shm_size: 128mb
19
19
  environment:
@@ -5,7 +5,7 @@ require_relative '../lib/rujira'
5
5
 
6
6
  Dotenv.load
7
7
 
8
- client = Rujira::Client.new('http://localhost:8080', dispatchable: false)
8
+ client = Rujira::Client.new('http://localhost:8080', wrap_responses: true)
9
9
 
10
10
  project_name = 'TEST124'
11
11
  project_key = project_name
@@ -5,7 +5,7 @@ require_relative '../lib/rujira'
5
5
 
6
6
  Dotenv.load
7
7
 
8
- client = Rujira::Client.new('http://localhost:8080', dispatchable: true)
8
+ client = Rujira::Client.new('http://localhost:8080')
9
9
 
10
10
  project_name = 'TEST124'
11
11
  project_key = project_name
@@ -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:
@@ -40,8 +40,8 @@ module Rujira
40
40
  # @return [Object] The API response after dispatching the request.
41
41
  def call
42
42
  @client.logger.debug "Call the method: #{caller_locations(1, 1)[0].label}"
43
- return @client.dispatch(@request) if @client.dispatchable
44
- return self if @client.commitable
43
+ return @client.dispatch(@request) unless @client.wrap_responses
44
+ return self if @client.lazy
45
45
 
46
46
  to_obj
47
47
  end
@@ -52,6 +52,7 @@ module Rujira
52
52
  def commit
53
53
  @client.dispatch(@request)
54
54
  end
55
+ alias execute commit
55
56
 
56
57
  # Converts the API response into structured Ruby objects.
57
58
  # If the response is an array of hashes, maps each element through `process`.
@@ -59,9 +60,9 @@ module Rujira
59
60
  #
60
61
  # @return [Object] Processed response as one or more resource objects, or original response.
61
62
  def to_obj
62
- response = commit
63
+ response = execute
63
64
 
64
- return response.map { |el| process(el) } if response.is_a?(Array) && response.all? { |el| el.is_a?(Hash) }
65
+ return response.map { |el| process(el) } if response.is_a?(Array) && response.all?(Hash)
65
66
  return response unless response.is_a?(Hash)
66
67
 
67
68
  process response
@@ -76,7 +77,12 @@ module Rujira
76
77
  def process(response)
77
78
  response.merge!(@metadata)
78
79
  resource_class_name = self.class.name.sub('Api', 'Resource')
79
- Object.const_get(resource_class_name).new(@client, **response)
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 wrap_responses mode.'
85
+ end
80
86
  end
81
87
 
82
88
  # Sets ownership context for the resource by storing a parent ID or key
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rujira
4
+ module Api
5
+ # API reference:
6
+ # https://docs.atlassian.com/software/jira/docs/api/REST/9.17.0/#api/2/issuetype
7
+ #
8
+ class IssueType < Common
9
+ def get(&block)
10
+ builder do
11
+ path 'issuetype'
12
+ method :get
13
+ instance_eval(&block) if block_given?
14
+ end
15
+ call
16
+ end
17
+
18
+ def get_by_name(name, &block)
19
+ issue_types = get(&block)
20
+ issue_types.find { |type| type.name == name }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rujira
4
+ module Api
5
+ # API reference:
6
+ # https://docs.atlassian.com/software/jira/docs/api/REST/9.17.0/#api/2/priority
7
+ #
8
+ class Priority < Common
9
+ def get(&block)
10
+ builder do
11
+ path 'priority'
12
+ method :get
13
+ instance_eval(&block) if block_given?
14
+ end
15
+ call
16
+ end
17
+
18
+ def get_by_name(name, &block)
19
+ priorities = get(&block)
20
+ priorities.find { |type| type.name == name }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rujira
4
+ module Api
5
+ class User < Common # rubocop:disable Style/Documentation
6
+ def create(&block)
7
+ builder do
8
+ path 'user'
9
+ method :post
10
+ instance_eval(&block) if block_given?
11
+ end
12
+ call
13
+ end
14
+
15
+ def update(key, &block)
16
+ builder do
17
+ path 'user'
18
+ method :put
19
+ params key: key
20
+ instance_eval(&block) if block_given?
21
+ end
22
+ call
23
+ end
24
+
25
+ def get(key = nil, include_deleted: false, &block)
26
+ builder do
27
+ path 'user'
28
+ method :get
29
+ params key: key, includeDeleted: include_deleted
30
+ instance_eval(&block) if block_given?
31
+ end
32
+ call
33
+ end
34
+
35
+ def delete(key, &block)
36
+ builder do
37
+ path 'user'
38
+ method :delete
39
+ params key: key
40
+ instance_eval(&block) if block_given?
41
+ end
42
+ call
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/rujira/client.rb CHANGED
@@ -11,7 +11,7 @@ module Rujira
11
11
  # client.issue.get("TEST-123")
12
12
  #
13
13
  class Client
14
- attr_reader :dispatchable, :logger, :commitable
14
+ attr_reader :wrap_responses, :logger, :lazy
15
15
 
16
16
  SUPPORTED_METHODS = %i[get delete head post put patch].freeze
17
17
 
@@ -23,9 +23,9 @@ module Rujira
23
23
  # @example Initialize client
24
24
  # client = Rujira::Client.new("https://jira.example.com", debug: true)
25
25
  #
26
- def initialize(url, debug: false, dispatchable: true, commitable: false, log_level: 'error') # rubocop:disable Metrics/MethodLength
27
- @dispatchable = dispatchable
28
- @commitable = commitable
26
+ def initialize(url, debug: false, wrap_responses: false, lazy: false, log_level: 'error') # rubocop:disable Metrics/MethodLength
27
+ @wrap_responses = wrap_responses
28
+ @lazy = lazy
29
29
  @uri = URI(url)
30
30
  @debug = ENV.fetch('RUJIRA_DEBUG', debug.to_s) == 'true'
31
31
  @raise_error = false
@@ -136,6 +136,8 @@ module Rujira
136
136
 
137
137
  @payload = payload
138
138
  end
139
+ alias body payload
140
+ alias data payload
139
141
 
140
142
  # Extends the current payload by merging the given hash into it.
141
143
  #
@@ -2,9 +2,17 @@
2
2
 
3
3
  module Rujira
4
4
  module Resource
5
- class Common
6
- def initialize(client, **_args)
5
+ class Common # rubocop:disable Style/Documentation
6
+ attr_reader :response
7
+
8
+ def initialize(client, **response)
7
9
  @client = client
10
+ @response = response
11
+ end
12
+
13
+ def reload!
14
+ user = @client.User.get(@key)
15
+ initialize(@client, **user.response)
8
16
  end
9
17
  end
10
18
  end
@@ -47,6 +47,10 @@ module Rujira
47
47
  @client.logger.debug "Deleting issue #{@key}"
48
48
  @client.Issue.delete(@id)
49
49
  end
50
+
51
+ def attach_file(path)
52
+ @client.Issue.attachments @key, path
53
+ end
50
54
  end
51
55
  end
52
56
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rujira
4
+ module Resource
5
+ # TODO
6
+ class IssueType < Common
7
+ # @return [String] The ID of the issue
8
+ attr_reader :id
9
+ # @return [String] The key of the issue (e.g., "TEST-123")
10
+ attr_reader :name
11
+ # @return [String] The URL to access this issue via Jira REST API
12
+ attr_reader :url
13
+
14
+ # Initializes an IssueType resource
15
+ #
16
+ # @param [Object] client The API client instance used to make requests
17
+ # @param [Hash] hash The issue data from the Jira API
18
+ def initialize(client, **args)
19
+ super
20
+
21
+ @id = args['id']
22
+ @name = args['name']
23
+ @url = args['self']
24
+ end
25
+ end
26
+ end
27
+ end
@@ -25,6 +25,18 @@ module Rujira
25
25
  @name = args['name']
26
26
  @email = args['emailAddress']
27
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
28
40
  end
29
41
  end
30
42
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rujira
4
+ module Resource
5
+ # TODO
6
+ class Priority < Common
7
+ # @return [String] The ID of the issue
8
+ attr_reader :id
9
+ # @return [String] The key of the issue (e.g., "TEST-123")
10
+ attr_reader :name
11
+ # @return [String] The URL to access this issue via Jira REST API
12
+ attr_reader :url
13
+
14
+ # Initializes an Priority resource
15
+ #
16
+ # @param [Object] client The API client instance used to make requests
17
+ # @param [Hash] hash The issue data from the Jira API
18
+ def initialize(client, **args)
19
+ super
20
+
21
+ @id = args['id']
22
+ @name = args['name']
23
+ @url = args['self']
24
+ end
25
+ end
26
+ end
27
+ end
@@ -30,6 +30,19 @@ module Rujira
30
30
  def delete
31
31
  @client.Project.delete(@id)
32
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
33
46
  end
34
47
  end
35
48
  end
@@ -0,0 +1,61 @@
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::User.new(client, data)
10
+ # issue.add_comment("This is a comment")
11
+ #
12
+ class User < Common
13
+ # @return [String] The URL to access this issue via Jira REST API
14
+ attr_reader :url
15
+ # @return [String] The key of the issue (e.g., "TEST-123")
16
+ attr_reader :key
17
+ attr_reader :name, :email, :display_name, :active
18
+
19
+ # Initializes an User resource
20
+ #
21
+ # @param [Object] client The API client instance used to make requests
22
+ # @param [Hash] hash The issue data from the Jira API
23
+ def initialize(client, **args)
24
+ super
25
+
26
+ @url = args['self']
27
+ @key = args['key']
28
+ @name = args['name']
29
+ @email = args['emailAddress']
30
+ @display_name = args['displayName']
31
+ @active = args.fetch('active', true)
32
+ @deleted = args.fetch('deleted', false)
33
+ end
34
+
35
+ def delete
36
+ @client.User.delete(@key)
37
+ end
38
+
39
+ def update(&block)
40
+ @client.User.update(@key) do
41
+ instance_eval(&block) if block_given?
42
+ end
43
+ reload!
44
+ end
45
+
46
+ def deactivate!
47
+ @client.User.update(@key) do
48
+ payload active: false
49
+ end
50
+ reload!
51
+ end
52
+
53
+ def activate!
54
+ @client.User.update(@key) do
55
+ payload active: true
56
+ end
57
+ reload!
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,31 +10,16 @@ module Rujira
10
10
  # TODO
11
11
  class Jira
12
12
  include Rake::DSL if defined?(Rake::DSL)
13
+ require_relative 'utils'
14
+
13
15
  def initialize
14
16
  generate
15
17
  end
16
18
 
17
- def client
18
- url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
19
- @client ||= Rujira::Client.new(url, debug: false)
20
- end
21
-
22
- def fetch_options(params, name)
23
- help = params.map { |k| "#{k}=<VALUE>" }.join(' ')
24
- options = params.to_h do |k|
25
- [k.downcase.to_sym, ENV.fetch(k, nil)]
26
- end
27
- missing = options.select { |_, v| v.nil? }.keys
28
- unless missing.empty?
29
- abort "❌ ERROR: The following required environment variables are missing: #{missing.join(', ').upcase}\n" \
30
- "✅ USAGE: rake #{name} #{help}"
31
- end
32
-
33
- options
34
- end
35
-
36
19
  def generate # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
37
- namespace :jira do
20
+ namespace :jira do # rubocop:disable Metrics/BlockLength
21
+ require_relative 'issue'
22
+
38
23
  desc 'Test connection by getting username'
39
24
  task :whoami do
40
25
  result = client.Myself.get
@@ -53,6 +38,18 @@ module Rujira
53
38
  result = client.Project.list
54
39
  puts JSON.pretty_generate(result)
55
40
  end
41
+
42
+ desc 'Create project'
43
+ task :create do |t|
44
+ options = fetch_options(%w[KEY NAME TYPE LEAD], t.name)
45
+ result = client.Project.create do
46
+ payload key: options[:key],
47
+ name: options[:name],
48
+ projectTypeKey: options[:type],
49
+ lead: options[:lead]
50
+ end
51
+ puts JSON.pretty_generate(result)
52
+ end
56
53
  end
57
54
 
58
55
  namespace :dashboard do
@@ -106,48 +103,6 @@ module Rujira
106
103
  end
107
104
  end
108
105
  end
109
-
110
- namespace :issue do
111
- desc 'Create a issue'
112
- task :create do |t|
113
- options = fetch_options(%w[PROJECT SUMMARY DESCRIPTION ISSUETYPE], t.name)
114
- abort 'ISSUETYPE must start with a capital letter' unless options[:issuetype].match?(/\A[A-Z]/)
115
-
116
- result = client.Issue.create do
117
- payload fields: {
118
- project: { key: options[:project] },
119
- summary: options[:summary],
120
- issuetype: { name: options[:issuetype] },
121
- description: options[:description]
122
- }
123
- end
124
- puts JSON.pretty_generate(result)
125
- end
126
-
127
- desc 'Search issue by fields'
128
- task :search do |t|
129
- options = fetch_options(%w[JQL], t.name)
130
- result = client.Search.get do
131
- data jql: options[:jql]
132
- end
133
- result['issues'].each { |i| puts JSON.pretty_generate(i) }
134
- end
135
-
136
- desc 'Delete issue'
137
- task :delete do |t|
138
- options = fetch_options(%w[ISSUE_ID], t.name)
139
-
140
- client.Issue.del options[:issue_id]
141
- end
142
-
143
- desc 'Example usage attaching in issue'
144
- task :attach do |t|
145
- options = fetch_options(%w[FILE ISSUE_ID], t.name)
146
-
147
- result = client.Issue.attachments options[:issue_id], @options[:file]
148
- puts JSON.pretty_generate(result)
149
- end
150
- end
151
106
  end
152
107
  end
153
108
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :issue do # rubocop:disable Metrics/BlockLength
4
+ desc 'Create a issue'
5
+ task :create do |t|
6
+ options = fetch_options(%w[PROJECT_KEY SUMMARY DESCRIPTION ISSUETYPE], t.name)
7
+ abort 'ISSUETYPE must start with a capital letter' unless options[:issuetype].match?(/\A[A-Z]/)
8
+
9
+ result = client.Issue.create do
10
+ payload fields: {
11
+ project: { key: options[:project_key] },
12
+ summary: options[:summary],
13
+ issuetype: { name: options[:issuetype] },
14
+ description: options[:description]
15
+ }
16
+ end
17
+ puts JSON.pretty_generate(result)
18
+ end
19
+
20
+ desc 'Generate a Jira link for creating a new issue'
21
+ task :generate_link do # rubocop:disable Metrics/BlockLength
22
+ require 'cgi'
23
+
24
+ me = client_wrapped.Myself.get
25
+ server_info = client.ServerInfo.get
26
+ base_url = server_info['baseUrl']
27
+
28
+ print 'Project key (e.g., ABC): '
29
+ project_key = $stdin.gets.chomp
30
+ project = client_wrapped.Project.get(project_key)
31
+
32
+ print 'Summary (short description): '
33
+ summary = $stdin.gets.chomp
34
+
35
+ puts 'Opening editor for description...'
36
+ description = open_editor
37
+
38
+ issue_types = client_wrapped.IssueType.get
39
+ names = issue_types.map(&:name).join('/')
40
+ print "Issue type (#{names}): "
41
+ issue_type_name = $stdin.gets.chomp
42
+ issue_type = issue_types.find do |i|
43
+ i.name.downcase == issue_type_name.downcase
44
+ end
45
+
46
+ encoded_summary = CGI.escape(summary)
47
+ encoded_description = CGI.escape(description)
48
+
49
+ jira_url = "#{base_url}/secure/CreateIssueDetails!init.jspa" \
50
+ "?pid=#{project.id}" \
51
+ "&summary=#{encoded_summary}" \
52
+ "&description=#{encoded_description}" \
53
+ "&reporter=#{me.name}" \
54
+ "&issuetype=#{issue_type.id}"
55
+
56
+ puts "\nGenerated issue creation link:"
57
+ puts jira_url
58
+ end
59
+
60
+ desc 'Search issue by fields'
61
+ task :search do |t|
62
+ options = fetch_options(%w[JQL], t.name)
63
+ result = client.Search.get do
64
+ data jql: options[:jql]
65
+ end
66
+ result['issues'].each { |i| puts JSON.pretty_generate(i) }
67
+ end
68
+
69
+ desc 'Delete issue'
70
+ task :delete do |t|
71
+ options = fetch_options(%w[ISSUE_ID], t.name)
72
+
73
+ client.Issue.del options[:issue_id]
74
+ end
75
+
76
+ desc 'Example usage attaching in issue'
77
+ task :attach do |t|
78
+ options = fetch_options(%w[FILE ISSUE_ID], t.name)
79
+
80
+ result = client.Issue.attachments options[:issue_id], @options[:file]
81
+ puts JSON.pretty_generate(result)
82
+ end
83
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ def client
4
+ url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
5
+ @client ||= Rujira::Client.new(url, debug: false)
6
+ end
7
+
8
+ def client_wrapped
9
+ url = ENV.fetch('RUJIRA_URL', 'http://localhost:8080')
10
+ @client_wrapped ||= Rujira::Client.new(url, debug: false, wrap_responses: true)
11
+ end
12
+
13
+ def open_editor(initial_text = '')
14
+ require 'tempfile'
15
+
16
+ editor = ENV['EDITOR'] || 'vi'
17
+
18
+ file = Tempfile.new(['jira_description', '.txt'])
19
+ file.write(initial_text)
20
+ file.flush
21
+ file.close
22
+
23
+ system("#{editor} #{file.path}")
24
+
25
+ content = File.read(file.path)
26
+ file.unlink
27
+
28
+ content
29
+ end
30
+
31
+ def fetch_options(params, name)
32
+ help = params.map { |k| "#{k}=<VALUE>" }.join(' ')
33
+ options = params.to_h do |k|
34
+ [k.downcase.to_sym, ENV.fetch(k, nil)]
35
+ end
36
+ missing = options.select { |_, v| v.nil? }.keys
37
+ unless missing.empty?
38
+ abort "❌ ERROR: The following required environment variables are missing: #{missing.join(', ').upcase}\n" \
39
+ "✅ USAGE: rake #{name} #{help}"
40
+ end
41
+
42
+ options
43
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rujira
4
- VERSION = '0.7.0'
4
+ VERSION = '0.7.3'
5
5
  end
data/lib/rujira.rb CHANGED
@@ -34,6 +34,12 @@ require_relative 'rujira/api/configuration'
34
34
  require_relative 'rujira/api/custom_fields'
35
35
  require_relative 'rujira/api/field'
36
36
  require_relative 'rujira/api/filter'
37
+ require_relative 'rujira/api/user'
38
+ require_relative 'rujira/resource/user'
39
+ require_relative 'rujira/api/issue_type'
40
+ require_relative 'rujira/resource/issue_type'
41
+ require_relative 'rujira/api/priority'
42
+ require_relative 'rujira/resource/priority'
37
43
 
38
44
  # Main Rujira module.
39
45
  # Serves as the namespace for the Jira SDK.
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.7.0
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Semenov
@@ -71,20 +71,28 @@ files:
71
71
  - lib/rujira/api/issue.rb
72
72
  - lib/rujira/api/issue/comments.rb
73
73
  - lib/rujira/api/issue/watchers.rb
74
+ - lib/rujira/api/issue_type.rb
74
75
  - lib/rujira/api/myself.rb
75
76
  - lib/rujira/api/permissions.rb
77
+ - lib/rujira/api/priority.rb
76
78
  - lib/rujira/api/project.rb
77
79
  - lib/rujira/api/search.rb
78
80
  - lib/rujira/api/server_info.rb
79
81
  - lib/rujira/api/sprint.rb
82
+ - lib/rujira/api/user.rb
80
83
  - lib/rujira/client.rb
81
84
  - lib/rujira/request.rb
82
85
  - lib/rujira/resource/comment.rb
83
86
  - lib/rujira/resource/common.rb
84
87
  - lib/rujira/resource/issue.rb
88
+ - lib/rujira/resource/issue_type.rb
85
89
  - lib/rujira/resource/myself.rb
90
+ - lib/rujira/resource/priority.rb
86
91
  - lib/rujira/resource/project.rb
92
+ - lib/rujira/resource/user.rb
87
93
  - lib/rujira/tasks/generate.rake
94
+ - lib/rujira/tasks/issue.rb
95
+ - lib/rujira/tasks/utils.rb
88
96
  - lib/rujira/version.rb
89
97
  - sig/rujira.rbs
90
98
  homepage: https://github.com/itmagelabs/rujira