lita-redmine2 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1241e2ab1d47e594c9cc6a43ce19ee3aeeddd52a
4
+ data.tar.gz: d528aea74380621f684e4cc2e6abee8e88bbcb42
5
+ SHA512:
6
+ metadata.gz: 2c431781f5103c893d28f924e4a907a418c68e1d9b24e41e2fcaa2378ea55766603617c37f6f25ec5d3491461eb7e7e096565da7efe28ac9dd121f796d5dbdf4
7
+ data.tar.gz: 6c3246cf86c6ae34fae42db9a993fd47be849694f15b57447ca877f9aa2da93e94c4bd584ed10f7ad2340f001f5adfc25345da201eed6468f3d7e373054a6a5d
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 NetBrick s.r.o.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # lita-redmine
2
+
3
+ Lita Redmine Plugin - Redmine handlers
4
+
5
+ ## Installation
6
+
7
+ Add lita-redmine to your Lita instance's Gemfile:
8
+
9
+ ``` ruby
10
+ gem "lita-redmine2", git: "https://github.com/netbrick/lita-redmine2", branch: "master"
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ add into lita_config.rb Redmine URL
16
+
17
+ ``` ruby
18
+ Lita.configure do |config|
19
+ ...
20
+ config.handlers.redmine.url = "https://redmine.site.cz"
21
+ config.handlers.redmine.secret_key = "<secret key to encrypt tokens in redis>"
22
+ ...
23
+ end
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```
29
+ redmine|rm list <object #> - Redmine list objects, calls Redmine API as /:object.json
30
+ redmine|rm time entry new [Issue_id] [Hours] [Activity name] [Comment] - Create new time entry with today's date
31
+
32
+ redmine|rm issue <issue #> - Displays issue id and subject
33
+ redmine|rm issue close <issue #> - Selects first status with closing attribute and updates issue to this status
34
+ redmine|rm issue detail <issue #> - Displays issue detail
35
+ redmine|rm issue journal <issue #> - Displays issue journal
36
+ redmine|rm issue link <issue #> - Displays issue link
37
+ redmine|rm issue new [Project name] [Subject] [Description] <firstname secondname> - Create new issue, name is optional and selects user to assign issue to
38
+ redmine|rm issue state change <issue #> <status_name> - Change issue status to specified
39
+
40
+ redmine|rm issues - List my issues
41
+ redmine|rm issue note <issue #> note - Add note to issue
42
+ redmine|rm issue assign <issue #> [firstname secondname] <note> - Assign issue to user, note is optional
43
+
44
+ redmine|rm projects - List my projects
45
+
46
+ redmine|rm register <api token> - Register API token
47
+ redmine|rm unregister - Unregister API token
48
+ redmine|rm token - Show registered token (PM)
49
+
50
+ redmine|rm info - Display info
51
+ <something>#<issue #><something> - Displays issue subject when mentoied in conversation
52
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,12 @@
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/redmine2"
8
+
9
+ Lita::Handlers::Redmine2.template_root File.expand_path(
10
+ File.join("..", "..", "templates"),
11
+ __FILE__
12
+ )
@@ -0,0 +1,531 @@
1
+ require "lita"
2
+ require "crypt/blowfish"
3
+ require "#{File.dirname(__FILE__)}/../../resources/redmine_resource"
4
+
5
+ module Lita
6
+ module Handlers
7
+ class Redmine2 < Handler
8
+ config :url
9
+ config :secret_key
10
+
11
+ route /^(redmine|rm)\slist\s([a-zA-Z_]+)/, :list_objects, help: { "redmine|rm list <object #>" => "Redmine list objects, calls Redmine API as /:object.json" }
12
+
13
+ route /^(redmine|rm)\stime entry\snew\s\[(\d+)\]\s\[([^\[\]]+)\]\s\[([^\[\]]+)\]\s\[([^\[\]]+)\]/, :time_entry_new, help: { "redmine|rm time entry new [Issue_id] [Hours] [Activity name] [Comment]" => "Create new time entry with today's date" }
14
+
15
+ route /^(redmine|rm)\sissue\s(\d+)/, :issue, help: { "redmine|rm issue <issue #>" => "Displays issue id and subject" }
16
+ route /^(redmine|rm)\sissue\sclose\s(\d+)/, :issue_close, help: { "redmine|rm issue close <issue #>" => "Selects first status with closing attribute and updates issue to this status" }
17
+ route /^(redmine|rm)\sissue\sdetail\s(\d+)/, :issue_detail, help: { "redmine|rm issue detail <issue #>" => "Displays issue detail" }
18
+ route /^(redmine|rm)\sissue\sjournal\s(\d+)/, :issue_journal, help: { "redmine|rm issue journal <issue #>" => "Displays issue journal" }
19
+ route /^(redmine|rm)\sissue\slink\s(\d+)/, :issue_link, help: { "redmine|rm issue link <issue #>" => "Displays issue link" }
20
+ route /^(redmine|rm)\sissue\snew\s\[([^\[\]]+)\]\s\[([^\[\]]+)\]\s\[([^\[\]]+)\](\s\[[^\[\]]+\])?/, :issue_new, help: { "redmine|rm issue new [Project name] [Subject] [Description] [Firstname Secondname]" => "Create new issue, name is optional and selects user to assign issue to" }
21
+ route /^(redmine|rm)\sissue\sstate\schange\s\[(\d+)\]\s\[([^\[\]]+)\]/, :issue_change_state, help: {"redmine|rm issue state change [Issue_id] [Status_name]" => "Change issue status to specified" }
22
+ route /^(redmine|rm)\sissues/, :issues, help: {"redmine|rm issues" => "List my issues" }
23
+ route /^(redmine|rm)\sissue\snote\s\[(\d+)\]\s\[([^\[\]]+)\]/, :issue_note, help: {"redmine|rm issue note [Issue_id] [Note]" => "Add note to issue" }
24
+ route /^(redmine|rm)\sissue\sassign\s\[(\d+)\]\s\[([^\[\]]+)\](\s\[[^\[\]]+\])?/, :issue_assign, help: {"redmine|rm issue assign [Issue_id] [Firstname Secondname] [Note]" => "Assign issue to user, note is optional" }
25
+
26
+ route /^(redmine|rm)\sprojects/, :projects, help: {"redmine|rm projects" => "List my projects" }
27
+
28
+ route /^(redmine|rm)\sregister\s([a-zA-z0-9])/, :register, help: { "redmine|rm register <api token>" => "Register API token" }
29
+ route /^(redmine|rm)\sunregister/, :unregister, help: { "redmine|rm unregister" => "Unregister API token" }
30
+ route /^(redmine|rm)\stoken/, :token, help: { "redmine|rm token" => "Show registered token (PM)"}
31
+
32
+ route /^(redmine|rm)\sinfo/, :info, help: {"redmine|rm info" => "Display info"}
33
+ route /.*\#(\d+).*/, :issue_mention, help: {"<something>#<issue #><something>" => "Displays issue subject when mentoied in conversation" }
34
+
35
+ def info(response)
36
+ response.reply "Lita Redmine Bot by NetBrick"
37
+ end
38
+
39
+ # get list of objects
40
+ def list_objects(response)
41
+ resource = get_user_token(response.user.id)
42
+
43
+ if resource.nil?
44
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
45
+ else
46
+ object = response.matches.flatten[1]
47
+
48
+ # Update route as there objects require 'enumerations' in the url
49
+ if object == 'time_entry_activities' || object == 'issue_priorities'
50
+ object = 'enumerations/' + object
51
+ end
52
+
53
+ message = resource.list(object: object)
54
+ if message[:resource_error].nil?
55
+ response.reply parse_list_objects(message)
56
+ else
57
+ response.reply_privately message[:resource_error]
58
+ end
59
+ end
60
+ end
61
+
62
+ # create new time entry
63
+ def time_entry_new(response)
64
+ resource = get_user_token(response.user.id)
65
+
66
+ if resource.nil?
67
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
68
+ else
69
+ issue_id = response.matches.flatten[1]
70
+ hours = response.matches.flatten[2]
71
+ activity_name = response.matches.flatten[3]
72
+ comments = response.matches.flatten[4]
73
+
74
+ if activity_id = get_activity_by_name(activity_name, resource)
75
+ message = resource.create_entry({issue_id: issue_id, hours: hours, activity_id: activity_id, comments: comments})
76
+
77
+ if message[:resource_error].nil?
78
+ response.reply message[:message]
79
+ else
80
+ response.reply_privately message[:resource_error]
81
+ end
82
+ else
83
+ response.reply_privately "Unable to find activity by specified name"
84
+ end
85
+ end
86
+ end
87
+
88
+ # show issue subject
89
+ def issue(response)
90
+ resource = get_user_token(response.user.id)
91
+
92
+ if resource.nil?
93
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
94
+ else
95
+ issue_id = response.matches.flatten[1]
96
+ issue = resource.issue(id: issue_id)
97
+
98
+ if issue[:resource_error].nil?
99
+ issue = issue['issue']
100
+ response.reply "#{issue['id']}: (#{issue['project']['name']}) #{issue["subject"]}"
101
+ else
102
+ response.reply_privately issue[:resource_error]
103
+ end
104
+ end
105
+ end
106
+
107
+ # show issue subject and description
108
+ def issue_detail(response)
109
+ resource = get_user_token(response.user.id)
110
+
111
+ if resource.nil?
112
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
113
+ else
114
+ issue_id = response.matches.flatten[1]
115
+ issue = resource.issue(id: issue_id)
116
+
117
+ if issue[:resource_error].nil?
118
+ issue = issue['issue']
119
+ response.reply "#{issue['id']}: (#{issue['project']['name']}) #{issue["subject"]}\n#{issue['description']}"
120
+ else
121
+ response.reply_privately issue[:resource_error]
122
+ end
123
+ end
124
+ end
125
+
126
+ # show issue subject and description
127
+ def issue_journal(response)
128
+ resource = get_user_token(response.user.id)
129
+
130
+ if resource.nil?
131
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
132
+ else
133
+ issue_id = response.matches.flatten[1]
134
+ issue = resource.issue(id: issue_id)
135
+
136
+ if issue[:resource_error].nil?
137
+ issue = issue['issue']
138
+
139
+ message = "#{issue['id']}: (#{issue['project']['name']}) #{issue["subject"]}\n#{issue['description']}"
140
+
141
+ issue['journals'] = issue['journals'].nil? ? {} : issue['journals']
142
+ issue['journals'].each do |j|
143
+ if !j['notes'].nil? && !j['notes'].empty?
144
+ message = message + "\n"
145
+ message = message + "Author: #{j['user']['name']}\n"
146
+ message = message + j['notes'] + "\n"
147
+ end
148
+ end
149
+
150
+ response.reply message
151
+ else
152
+ response.reply_privately issue[:resource_error]
153
+ end
154
+ end
155
+ end
156
+
157
+ # show issue link
158
+ def issue_link(response)
159
+ response.reply "Issue ##{response.matches.flatten[1]}: #{config.url}/issues/#{response.matches.flatten[1]}"
160
+ end
161
+
162
+ # change status of issue
163
+ def issue_change_state(response)
164
+ resource = get_user_token(response.user.id)
165
+
166
+ if resource.nil?
167
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
168
+ else
169
+ issue_id = response.matches.flatten[1]
170
+ status_name = response.matches.flatten[2]
171
+
172
+ if status_id = get_status_by_name(status_name, resource)
173
+ issue = resource.update_issue({ status_id: status_id }, id: issue_id)
174
+ if issue[:resource_error].nil?
175
+ response.reply issue[:message]
176
+ else
177
+ response.reply_privately issue[:resource_error]
178
+ end
179
+ else
180
+ response.reply_privately "Unable to find status by specified name"
181
+ end
182
+ end
183
+ end
184
+
185
+ # close issue
186
+ def issue_close(response)
187
+ resource = get_user_token(response.user.id)
188
+
189
+ if resource.nil?
190
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
191
+ else
192
+ issue_id = response.matches.flatten[1]
193
+
194
+ if status_id = get_closing_status_id(resource)
195
+ issue = resource.update_issue({ status_id: status_id }, id: issue_id)
196
+ if issue[:resource_error].nil?
197
+ response.reply issue[:message]
198
+ else
199
+ response.reply_privately issue[:resource_error]
200
+ end
201
+ else
202
+ response.reply_privately "Unable to find any closing status"
203
+ end
204
+ end
205
+ end
206
+
207
+ # assign issue to user specified by name
208
+ def issue_assign(response)
209
+ resource = get_user_token(response.user.id)
210
+
211
+ if resource.nil?
212
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
213
+ else
214
+ issue_id = response.matches.flatten[1]
215
+ name = response.matches.flatten[2]
216
+ note = response.matches.flatten[3].nil? ? "" : response.matches.flatten[3].strip
217
+ note = note[1, note.length - 2]
218
+
219
+ if user_id = get_user_by_name(name, resource)
220
+ issue = resource.update_issue({ assigned_to_id: user_id, notes: note }, id: issue_id)
221
+ if issue[:resource_error].nil?
222
+ response.reply issue[:message]
223
+ else
224
+ response.reply_privately issue[:resource_error]
225
+ end
226
+ else
227
+ response.reply_privately "Unable to find user by name"
228
+ end
229
+ end
230
+ end
231
+
232
+ # create issue in project specified by name
233
+ def issue_new(response)
234
+ resource = get_user_token(response.user.id)
235
+
236
+ if resource.nil?
237
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
238
+ else
239
+ project_name = response.matches.flatten[1]
240
+ subject = response.matches.flatten[2]
241
+ description = response.matches.flatten[3]
242
+ assignee_name = response.matches.flatten[4].nil? ? "" : response.matches.flatten[4].strip
243
+ assignee_name = assignee_name[1, assignee_name.length - 2]
244
+
245
+ project_id = get_project_by_name(project_name, resource)
246
+ user_id = get_user_by_name(assignee_name, resource)
247
+
248
+ if project_id
249
+ issue = resource.create_issue({ project_id: project_id, subject: subject, description: description, assigned_to_id: user_id })
250
+ if issue[:resource_error].nil?
251
+ issue = issue['issue']
252
+ response.reply "Issue created with id #{issue['id']}"
253
+ else
254
+ response.reply_privately issue[:resource_error]
255
+ end
256
+ else
257
+ response.reply_privately "Unable to find project by name"
258
+ end
259
+ end
260
+ end
261
+
262
+ # show issue subject when someone mention issue #
263
+ def issue_mention(response)
264
+ resource = get_user_token(response.user.id)
265
+
266
+ if resource.nil?
267
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
268
+ else
269
+ issue_id = response.matches.flatten[0]
270
+ issue = resource.issue(id: issue_id)
271
+
272
+ if issue[:resource_error].nil?
273
+ issue = issue['issue']
274
+ response.reply "#{issue['id']}: (#{issue['project']['name']}) #{issue["subject"]}"
275
+ else
276
+ response.reply_privately issue[:resource_error]
277
+ end
278
+ end
279
+ end
280
+
281
+ # write new issue note
282
+ def issue_note(response)
283
+ resource = get_user_token(response.user.id)
284
+
285
+ if resource.nil?
286
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
287
+ else
288
+ issue_id = response.matches.flatten[1]
289
+ issue_note = response.matches.flatten[2]
290
+
291
+ issue = resource.update_issue({ notes: issue_note }, id: issue_id)
292
+ if issue[:resource_error].nil?
293
+ response.reply issue[:message]
294
+ else
295
+ response.reply_privately issue[:resource_error]
296
+ end
297
+ end
298
+ end
299
+
300
+ # list issues of user
301
+ def issues(response)
302
+ resource = get_user_token(response.user.id)
303
+
304
+ if resource.nil?
305
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
306
+ else
307
+ issues = resource.issues
308
+
309
+ if issues[:resource_error].nil?
310
+ message = ''
311
+ issues = issues['issues'].nil? ? {} : issues['issues']
312
+ issues.each do |issue|
313
+ message << "#{issue['id']}: (#{issue['project']['name']}) #{issue['subject']}\n"
314
+ end
315
+
316
+ response.reply message
317
+ else
318
+ response.reply_privately issue[:resource_error]
319
+ end
320
+ end
321
+ end
322
+
323
+ # list projects of user
324
+ def projects(response)
325
+ resource = get_user_token(response.user.id)
326
+
327
+ if resource.nil?
328
+ response.reply_privately "Token not registered, register token via 'redmine register <api_token>'"
329
+ else
330
+ projects = resource.projects
331
+
332
+ if projects[:resource_error].nil?
333
+ message = ''
334
+ projects = projects['projects'].nil? ? {} : projects['projects']
335
+ projects.each do |prj|
336
+ message << "#{prj['id']}: (#{prj['name']})\n"
337
+ end
338
+
339
+ response.reply message
340
+ else
341
+ response.reply_privately projects[:resource_error]
342
+ end
343
+ end
344
+ end
345
+
346
+ # save user API token into redis
347
+ def register(response)
348
+ user_id = response.user.id
349
+ api_token = response.args[1]
350
+
351
+ api_token = encryptor.encrypt_string(api_token)
352
+ redis.set("user_#{user_id}", api_token)
353
+
354
+ message = "Registered: #{user_id} url #{config.url}"
355
+ response.reply message
356
+ end
357
+
358
+ # send saved API token to DM/PM
359
+ def token(response)
360
+ user_id = response.user.id
361
+
362
+ token = redis.get("user_#{user_id}")
363
+ token = decrypt_token(token, user_id)
364
+
365
+ message = "Token: #{token}"
366
+ response.reply_privately message
367
+ end
368
+
369
+ # delete saved API token
370
+ def unregister(response)
371
+ user_id = response.user.id
372
+
373
+ redis.set("user_#{user_id}", "")
374
+
375
+ message = "Unregistered: #{user_id}"
376
+ response.reply message
377
+ end
378
+
379
+ # retrieve user API token from redis
380
+ def get_user_token(user_id)
381
+ token = redis.get("user_#{user_id}")
382
+ token = decrypt_token(token, user_id)
383
+
384
+ if !token.nil? && !token.empty?
385
+ connection = Faraday.new(url: config.url, headers: { "X-Redmine-API-Key" => token })
386
+ resource = RedmineResource.new(connection: connection)
387
+ else
388
+ resource = nil
389
+ end
390
+ end
391
+
392
+ # find user id by name via API
393
+ def get_user_by_name(name, resource)
394
+ users = resource.users
395
+ users = users[:resource_error].nil? ? users['users'] : {}
396
+
397
+ id = nil
398
+ users.each do |user|
399
+ user_name = user['firstname'] + " " + user['lastname']
400
+ id = user['id'] if user_name == name
401
+ end
402
+
403
+ id
404
+ end
405
+
406
+ # find user id by name via API
407
+ def get_project_by_name(name, resource)
408
+ projects = resource.projects
409
+ projects = projects[:resource_error].nil? ? projects['projects'] : {}
410
+
411
+ id = nil
412
+ projects.each do |prj|
413
+ id = prj['id'] if prj['name'] == name
414
+ end
415
+
416
+ id
417
+ end
418
+
419
+ # get activity id by name
420
+ def get_activity_by_name(name, resource)
421
+ activities = resource.list(object: 'enumerations/time_entry_activities')
422
+ activities = activities[:resource_error].nil? ? activities['time_entry_activities'] : {}
423
+
424
+ activity_id = nil
425
+ activities.each do |a|
426
+ activity_id = a['id'] if a['name'] == name
427
+ end
428
+
429
+ activity_id
430
+ end
431
+
432
+ def get_status_by_name(name, resource)
433
+ states = resource.list(object: 'issue_statuses')
434
+ states = states['issue_statuses'].nil? ? [] : states['issue_statuses']
435
+
436
+ status_id = nil
437
+ states.each do |s|
438
+ status_id = s['id'] if s['name'] == name
439
+ end
440
+
441
+ status_id
442
+ end
443
+
444
+ # get status id with close attribute
445
+ def get_closing_status_id(resource)
446
+ states = resource.list(object: 'issue_statuses')
447
+ states = states['issue_statuses'].nil? ? [] : states['issue_statuses']
448
+
449
+ status_id = nil
450
+ states.each do |s|
451
+ if s['is_closed']
452
+ status_id = s['id']
453
+ break
454
+ end
455
+ end
456
+
457
+ status_id
458
+ end
459
+
460
+ # parse list of objects
461
+ def parse_list_objects(list)
462
+ message = ''
463
+
464
+ case list.keys[0]
465
+ when 'users'
466
+ users = list['users']
467
+ users.each do |u|
468
+ message << "#{u['id']}: #{u['firstname']} #{u['lastname']}\n"
469
+ end
470
+ when 'issues'
471
+ issues = list['issues']
472
+ issues.each do |i|
473
+ message << "#{i['id']}: (#{i['project']['name']}) #{i['subject']}\n"
474
+ end
475
+ when 'projects'
476
+ projects = list['projects']
477
+ projects.each do |p|
478
+ message << "#{p['id']}: (#{p['name']})\n"
479
+ end
480
+ when 'time_entries'
481
+ time_entries = list['time_entries']
482
+ time_entries.each do |t|
483
+ message << "Issue_id: #{t['issue']['id']}, spent_on: #{t['spent_on']}, hours: #{t['hours']}\n"
484
+ end
485
+ when 'time_entry_activities'
486
+ time_entry_activities = list['time_entry_activities']
487
+ time_entry_activities.each do |t|
488
+ message << "Name: #{t['name']}\n"
489
+ end
490
+ when 'issue_statuses'
491
+ issue_statuses = list['issue_statuses']
492
+ issue_statuses.each do |s|
493
+ message << "Name: #{s['name']}, closes issue: #{s['is_closed'] ? 'yes' : 'no'}\n"
494
+ end
495
+ when 'trackers'
496
+ trackers = list['trackers']
497
+ trackers.each do |t|
498
+ message << "Name: #{t['name']}\n"
499
+ end
500
+ when 'issue_priorities'
501
+ issue_priorities = list['issue_priorities']
502
+ issue_priorities.each do |p|
503
+ message << "Name: #{p['name']}\n"
504
+ end
505
+ else
506
+ message = 'Currently unsupported'
507
+ end
508
+
509
+ message
510
+ end
511
+
512
+ # try decrypt token and if not ecrypted, update redis record
513
+ def decrypt_token(token, user_id)
514
+ decrypted_token = encryptor.decrypt_string(token)
515
+ rescue
516
+ encrypted_token = encryptor.encrypt_string(token)
517
+ redis.set("user_#{user_id}", encrypted_token)
518
+
519
+ token
520
+ end
521
+
522
+ # encrypting mechanism
523
+ def encryptor
524
+ secret_key = config.secret_key.nil? ? "BqJYq7FQjhXuaPSkbTxw" : config.secret_key
525
+ @encryptor ||= Crypt::Blowfish.new(secret_key)
526
+ end
527
+ end
528
+
529
+ Lita.register_handler(Redmine2)
530
+ end
531
+ end
@@ -0,0 +1,70 @@
1
+ require 'resource_kit'
2
+ require 'faraday'
3
+ require 'json'
4
+
5
+ class RedmineResource < ResourceKit::Resource
6
+ resources do
7
+ default_handler (401) { |response| { resource_error: "Wrong API token, please register new one" } }
8
+ default_handler (403) { |response| { resource_error: "You are not authorized to do this action" } }
9
+ default_handler (404) { |response| { resource_error: "Server responded with error code 404, please check you have correct parameters" } }
10
+ default_handler (422) { |response| { resource_error: "Wrong parameters" } }
11
+ default_handler { |response| { resource_error: "Unknown error, status: #{response.status}" } }
12
+
13
+ action :list do
14
+ verb :get
15
+ path '/:object.json?limit=1000'
16
+ handler (:ok) { |response| JSON.parse(response.body) }
17
+ end
18
+
19
+ action :create_entry do
20
+ verb :post
21
+ path '/time_entries.json'
22
+ body { |object| { time_entry: object } }
23
+ handler (:created) { |response| { message: "Time entry created" } }
24
+ end
25
+
26
+ action :users do
27
+ verb :get
28
+ path '/users.json?limit=1000'
29
+ handler (:ok) { |response| JSON.parse(response.body) }
30
+ end
31
+
32
+ action :projects do
33
+ verb :get
34
+ path '/projects.json?limit=1000'
35
+ handler (:ok) { |response| JSON.parse(response.body) }
36
+ end
37
+
38
+ action :create_issue do
39
+ verb :post
40
+ path '/issues.json'
41
+ body { |object| { issue: object } }
42
+ handler (:created) { |response| JSON.parse(response.body) }
43
+ end
44
+
45
+ action :update_issue do
46
+ verb :put
47
+ path '/issues/:id.json'
48
+ body { |object| { issue: object } }
49
+ handler (:ok) { |response| { message: "Issue updated" } }
50
+ end
51
+
52
+ action :issues do
53
+ verb :get
54
+ path '/issues.json?assigned_to_id=me'
55
+ handler (:ok) { |response| JSON.parse(response.body) }
56
+ end
57
+
58
+ action :issue do
59
+ verb :get
60
+ path '/issues/:id.json'
61
+ handler (:ok) { |response| JSON.parse(response.body) }
62
+ end
63
+
64
+ action :issue_journals do
65
+ verb :get
66
+ path '/issues/:id.json?include=journals'
67
+ handler (:ok) { |response| JSON.parse(response.body) }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-redmine2"
3
+ spec.version = "0.1.0"
4
+ spec.authors = ["Jindrich Skupa / David Herman"]
5
+ spec.email = ["dev@netbrick.eu"]
6
+ spec.description = "Lita handlers for Redmine."
7
+ spec.summary = "Lita handlers for Redmine issue management."
8
+ spec.homepage = "https://github.com/netbrick/lita-redmine2"
9
+ spec.license = "MIT"
10
+ spec.metadata = { "lita_plugin_type" => "handler" }
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_runtime_dependency "lita", ">= 4.3"
18
+ spec.add_runtime_dependency "resource_kit"
19
+ spec.add_runtime_dependency "json"
20
+ spec.add_runtime_dependency "crypt"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "pry-byebug"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rack-test"
26
+ spec.add_development_dependency "rspec", ">= 3.0.0"
27
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,4 @@
1
+ en:
2
+ lita:
3
+ handlers:
4
+ redmine:
@@ -0,0 +1,4 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Redmine, lita_handler: true do
4
+ end
@@ -0,0 +1,6 @@
1
+ require "lita-redmine"
2
+ require "lita/rspec"
3
+
4
+ # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
5
+ # was generated with Lita 4, the compatibility mode should be left disabled.
6
+ Lita.version_3_compatibility_mode = false
File without changes
File without changes
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-redmine2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jindrich Skupa / David Herman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lita
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '4.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '4.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: resource_kit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: crypt
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rack-test
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: 3.0.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: 3.0.0
139
+ description: Lita handlers for Redmine.
140
+ email:
141
+ - dev@netbrick.eu
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - .gitignore
147
+ - Gemfile
148
+ - LICENSE
149
+ - README.md
150
+ - Rakefile
151
+ - lib/lita-redmine2.rb
152
+ - lib/lita/handlers/redmine2.rb
153
+ - lib/resources/redmine_resource.rb
154
+ - lita-redmine.gemspec
155
+ - locales/en.yml
156
+ - spec/lita/handlers/redmine_spec.rb
157
+ - spec/spec_helper.rb
158
+ - templates/.gitkeep
159
+ - templates/issue.slack.erb
160
+ - templates/issue_detail.slack.erb
161
+ - templates/issues.slack.erb
162
+ homepage: https://github.com/netbrick/lita-redmine2
163
+ licenses:
164
+ - MIT
165
+ metadata:
166
+ lita_plugin_type: handler
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.0.14
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Lita handlers for Redmine issue management.
187
+ test_files:
188
+ - spec/lita/handlers/redmine_spec.rb
189
+ - spec/spec_helper.rb