roust 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cef798971363e9cf2f7413d94e90b84573281480
4
- data.tar.gz: f6310fab56e67982258e61b0c038aac6b44a266d
3
+ metadata.gz: b2f1667d37f6c04f786ddb9bd4c437290151f549
4
+ data.tar.gz: e65c73bab11bd66ec7c904445de4aa7573440f69
5
5
  SHA512:
6
- metadata.gz: 14173684c35624ecc5ff42142de0c26b9a233c42f96c9f8a5fa43134f23b2a77ccca45d498541b41a6538d2eebd3f05eeb4935cab549d9fcc1bb29feb5ec354d
7
- data.tar.gz: 6e64614597cc7f8c3df383e75bfdba00f05b425b768b79bc9a922adcf8f6893de2c34b49aeab65c78cb7dfa535557c7c413b54bf248f53c367cd1dfb48ee838a
6
+ metadata.gz: 0aa451f3c531b9981ecd88f47d738122ed38684a3ec41ac27eb743b1bdb528c9377b42feb9e352f65039bc24a8458faa94d3717df009ebf0681da230ff77efc8
7
+ data.tar.gz: fdcf546e35a39bd594c9f2d5fa42d1232675a9019f6f019d77caf52e04fef5254bf4a15b452aad381c38feaa8a575941586256e30dc15e98674da7a65eff9d55
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ inherit_from:
2
+ - .rubocop_roust.yml
3
+ - .rubocop_todo.yml
@@ -0,0 +1,11 @@
1
+ # These are project syntax rules. We won't ever fix these. Unless we all get fired or something.
2
+
3
+ HashSyntax:
4
+ Enabled: false
5
+
6
+ LineLength:
7
+ Enabled: false
8
+
9
+ # Configuration parameters: EnforcedStyleForEmptyBraces, SupportedStyles.
10
+ Style/SpaceInsideHashLiteralBraces:
11
+ EnforcedStyle: no_space
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,15 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2014-06-30 14:32:46 +1000 using RuboCop version 0.24.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 12
9
+ Style/Documentation:
10
+ Enabled: false
11
+
12
+ # Offense count: 1
13
+ # Configuration parameters: Exclude.
14
+ Style/FileName:
15
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - '1.9.3'
4
+ - '2.0'
5
+ - '2.1'
6
+ script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -10,4 +10,5 @@ group :development do
10
10
  gem 'rake'
11
11
  gem 'webmock'
12
12
  gem 'pry'
13
+ gem 'rubocop', :require => false
13
14
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roust (1.3.0)
4
+ roust (1.4.0)
5
5
  activesupport (>= 4.1.0)
6
6
  httparty (>= 0.13.1)
7
7
  mail (>= 2.5.4)
@@ -16,11 +16,12 @@ GEM
16
16
  thread_safe (~> 0.1)
17
17
  tzinfo (~> 1.1)
18
18
  addressable (2.3.5)
19
+ ast (2.0.0)
19
20
  coderay (1.0.9)
20
21
  colorize (0.5.8)
21
22
  crack (0.4.1)
22
23
  safe_yaml (~> 0.9.0)
23
- diff-lcs (1.1.3)
24
+ diff-lcs (1.2.5)
24
25
  httparty (0.13.1)
25
26
  json (~> 1.8)
26
27
  multi_xml (>= 0.5.2)
@@ -33,22 +34,38 @@ GEM
33
34
  mime-types (1.25.1)
34
35
  minitest (5.3.3)
35
36
  multi_xml (0.5.5)
37
+ parser (2.2.0.pre.3)
38
+ ast (>= 1.1, < 3.0)
39
+ slop (~> 3.4, >= 3.4.5)
36
40
  polyglot (0.3.4)
41
+ powerpack (0.0.9)
37
42
  pry (0.9.12.2)
38
43
  coderay (~> 1.0.5)
39
44
  method_source (~> 0.8)
40
45
  slop (~> 3.4)
46
+ rainbow (2.0.0)
41
47
  rake (10.1.0)
42
- rspec (2.9.0)
43
- rspec-core (~> 2.9.0)
44
- rspec-expectations (~> 2.9.0)
45
- rspec-mocks (~> 2.9.0)
46
- rspec-core (2.9.0)
47
- rspec-expectations (2.9.1)
48
- diff-lcs (~> 1.1.3)
49
- rspec-mocks (2.9.0)
48
+ rspec (3.0.0)
49
+ rspec-core (~> 3.0.0)
50
+ rspec-expectations (~> 3.0.0)
51
+ rspec-mocks (~> 3.0.0)
52
+ rspec-core (3.0.2)
53
+ rspec-support (~> 3.0.0)
54
+ rspec-expectations (3.0.2)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.0.0)
57
+ rspec-mocks (3.0.2)
58
+ rspec-support (~> 3.0.0)
59
+ rspec-support (3.0.2)
60
+ rubocop (0.24.1)
61
+ json (>= 1.7.7, < 2)
62
+ parser (>= 2.2.0.pre.3, < 3.0)
63
+ powerpack (~> 0.0.6)
64
+ rainbow (>= 1.99.1, < 3.0)
65
+ ruby-progressbar (~> 1.4)
66
+ ruby-progressbar (1.5.1)
50
67
  safe_yaml (0.9.7)
51
- slop (3.4.6)
68
+ slop (3.5.0)
52
69
  thread_safe (0.3.3)
53
70
  treetop (1.4.15)
54
71
  polyglot
@@ -68,4 +85,5 @@ DEPENDENCIES
68
85
  rake
69
86
  roust!
70
87
  rspec
88
+ rubocop
71
89
  webmock
data/README.md CHANGED
@@ -85,6 +85,7 @@ bundle exec rake
85
85
 
86
86
  Roust has reasonable test coverage of the core features mentioned above. It has some other features that have been ported from the original rt-client implementation that are not tested (and are probably broken). See the TODO section for more details.
87
87
 
88
+ [![build status](https://travis-ci.org/bulletproofnetworks/roust.svg?branch=master)](https://travis-ci.org/bulletproofnetworks/roust)
88
89
 
89
90
  Releasing
90
91
  ---------
data/Rakefile CHANGED
@@ -6,22 +6,22 @@ require 'colorize'
6
6
 
7
7
  RSpec::Core::RakeTask.new('spec')
8
8
 
9
- desc "build gem"
9
+ desc 'build gem'
10
10
  task :build do
11
11
  build_output = `gem build roust.gemspec`
12
12
  puts build_output
13
13
 
14
- gem_filename = build_output[/File: (.*)/,1]
15
- pkg_path = "pkg"
14
+ gem_filename = build_output[/File: (.*)/, 1]
15
+ pkg_path = 'pkg'
16
16
  FileUtils.mkdir_p(pkg_path)
17
17
  FileUtils.mv(gem_filename, pkg_path)
18
18
 
19
19
  puts "Gem built in #{pkg_path}/#{gem_filename}".green
20
20
  end
21
21
 
22
- desc "push gem"
22
+ desc 'push gem'
23
23
  task :push do
24
- filenames = Dir.glob("pkg/*.gem")
24
+ filenames = Dir.glob('pkg/*.gem')
25
25
  filenames_with_times = filenames.map do |filename|
26
26
  [filename, File.mtime(filename)]
27
27
  end
@@ -34,4 +34,3 @@ task :push do
34
34
  end
35
35
 
36
36
  task :default => [:spec]
37
-
@@ -0,0 +1,5 @@
1
+ class Unauthenticated < Exception; end
2
+ class Unauthorized < Exception; end
3
+ class BadRequest < Exception; end
4
+ class UnhandledResponse < Exception; end
5
+ class InvalidRecord < Exception; end
@@ -0,0 +1,21 @@
1
+ module Roust::Queue
2
+ # id can be numeric (e.g. 28) or textual (e.g. sales)
3
+ def queue_show(id)
4
+ response = self.class.get("/queue/#{id}")
5
+
6
+ body, _ = explode_response(response)
7
+ if body =~ /No queue named/
8
+ nil
9
+ else
10
+ body.gsub!(/\n\s*\n/, "\n") # remove blank lines for Mail
11
+ message = Mail.new(body)
12
+ Hash[message.header.fields.map { |header|
13
+ key = header.name.to_s.downcase
14
+ value = header.value.to_s
15
+ [ key, value ]
16
+ }]
17
+ end
18
+ end
19
+
20
+ alias_method :queue, :queue_show
21
+ end
@@ -0,0 +1,245 @@
1
+ module Roust::Ticket
2
+ def ticket_show(id)
3
+ response = self.class.get("/ticket/#{id}/show")
4
+
5
+ body, _ = explode_response(response)
6
+
7
+ return nil if body =~ /^# (Ticket (\d+) does not exist\.)/
8
+
9
+ # Replace CF spaces with underscores
10
+ while body.match(/CF\.\{[\w_ ]*[ ]+[\w ]*\}/)
11
+ body.gsub!(/CF\.\{([\w_ ]*)([ ]+)([\w ]*)\}/, 'CF.{\1_\3}')
12
+ end
13
+
14
+ # Sometimes the API returns requestors formatted like this:
15
+ #
16
+ # Requestors: foo@example.org,
17
+ # bar@example.org, baz@example.org
18
+ # qux@example.org, quux@example.org,
19
+ # corge@example.org
20
+ #
21
+ # Turn it into this:
22
+ #
23
+ # Requestors: foo@example.org, bar@example.org, baz@example.org, ...
24
+ #
25
+ body.gsub!(/\n\n/, "\n")
26
+
27
+ %w(Requestors Cc AdminCc).each do |field|
28
+ body.gsub!(/^#{field}:(.+)^\n/m) do |m|
29
+ m.strip.split(/,\s+/).join(', ').strip
30
+ end
31
+ end
32
+
33
+ message = Mail.new(body)
34
+
35
+ hash = Hash[message.header.fields.map { |header|
36
+ key = header.name.to_s
37
+ value = header.value.to_s
38
+ [ key, value ]
39
+ }]
40
+
41
+ %w(Requestors Cc AdminCc).each do |field|
42
+ hash[field] = hash[field].split(', ') if hash[field]
43
+ end
44
+
45
+ hash['id'] = hash['id'].split('/').last
46
+
47
+ hash
48
+ end
49
+
50
+ def ticket_create(attrs)
51
+ default_attrs = {
52
+ 'id' => 'ticket/new'
53
+ }
54
+ attrs = default_attrs.merge(attrs).stringify_keys!
55
+
56
+ error = create_invalid?(attrs)
57
+ raise InvalidRecord, error if error
58
+
59
+ attrs['Text'].gsub!(/\n/, "\n ") if attrs['Text'] # insert a space on continuation lines.
60
+
61
+ # We can't set more than one AdminCc when creating a ticket. WTF RT.
62
+ #
63
+ # Delete it from the ticket we are creating, and we'll update the ticket
64
+ # after we've created.
65
+ admincc = attrs.delete('AdminCc')
66
+
67
+ content = compose_content('ticket', attrs['id'], attrs)
68
+
69
+ response = self.class.post(
70
+ '/ticket/new',
71
+ :body => {
72
+ :content => content
73
+ }
74
+ )
75
+
76
+ body, _ = explode_response(response)
77
+
78
+ case body
79
+ when /^# Ticket (\d+) created/
80
+ id = body[/^# Ticket (\d+) created/, 1]
81
+ # Add the AdminCc after the ticket is created, because we can't set it
82
+ # on ticket creation.
83
+ update(id, 'AdminCc' => admincc) if admincc
84
+
85
+ # Return the whole ticket, not just the id.
86
+ show(id)
87
+ when /^# Could not create ticket/
88
+ raise BadRequest, body
89
+ when /^# Syntax error/
90
+ raise SyntaxError, body
91
+ else
92
+ raise UnhandledResponse, body
93
+ end
94
+ end
95
+
96
+ def ticket_update(id, attrs)
97
+ content = compose_content('ticket', id, attrs)
98
+
99
+ response = self.class.post(
100
+ "/ticket/#{id}/edit",
101
+ :body => {
102
+ :content => content
103
+ },
104
+ )
105
+
106
+ body, _ = explode_response(response)
107
+
108
+ case body
109
+ when /^# Ticket (\d+) updated/
110
+ id = body[/^# Ticket (\d+) updated/, 1]
111
+ show(id)
112
+ when /^# You are not allowed to modify ticket \d+/
113
+ raise Unauthorized, body
114
+ when /^# Syntax error/
115
+ raise SyntaxError, body
116
+ else
117
+ raise UnhandledResponse, body
118
+ end
119
+ end
120
+
121
+ def ticket_search(query)
122
+ params = {
123
+ :query => query,
124
+ :format => 's',
125
+ :orderby => '+id'
126
+ }
127
+ response = self.class.get('/search/ticket', :query => params)
128
+ # FIXME(auxesis) use explode_response here
129
+
130
+ body, _ = explode_response(response)
131
+ body.split("\n").map do |t|
132
+ id, subject = t.split(': ', 2)
133
+ {'id' => id, 'Subject' => subject}
134
+ end
135
+ end
136
+
137
+ def ticket_history(id, opts = {})
138
+ options = {
139
+ :format => 'short',
140
+ :comments => false
141
+ }.merge(opts)
142
+
143
+ format = options[:format]
144
+ comments = options[:comments]
145
+ params = {
146
+ :format => format[0]
147
+ }
148
+
149
+ response = self.class.get("/ticket/#{id}/history", :query => params)
150
+
151
+ body, _ = explode_response(response)
152
+
153
+ case format
154
+ when 'short'
155
+ parse_short_history(body, :comments => comments)
156
+ when 'long'
157
+ parse_long_history(body, :comments => comments)
158
+ end
159
+ end
160
+
161
+ # TODO(auxesis): add method for getting ticket links
162
+ # TODO(auxesis): add method for updating ticket links
163
+ # TODO(auxesis): add method for listing ticket attachments
164
+ # TODO(auxesis): add method for getting a ticket attachment
165
+ # TODO(auxesis): add method for commenting on a ticket
166
+ # TODO(auxesis): add method for replying on a ticket
167
+
168
+ # To maintain backwards compatibility with previous versions (and rt-client),
169
+ # alias these methods to their short form.
170
+ alias_method :create, :ticket_create
171
+ alias_method :show, :ticket_show
172
+ alias_method :update, :ticket_update
173
+ alias_method :history, :ticket_history
174
+ alias_method :search, :ticket_search
175
+
176
+ private
177
+
178
+ def create_invalid?(attrs)
179
+ missing = %w(id Subject Queue).select { |k| !attrs.include?(k) }
180
+
181
+ if missing.empty?
182
+ return false
183
+ else
184
+ "Needs attributes: #{missing.join(', ')}"
185
+ end
186
+ end
187
+
188
+ def parse_short_history(body, opts = {})
189
+ comments = opts[:comments]
190
+ regex = comments ? '^\d+:' : '^\d+: [^Comments]'
191
+ history = body.split("\n").select { |l| l =~ /#{regex}/ }
192
+ history.map { |l| l.split(': ', 2) }
193
+ end
194
+
195
+ def parse_long_history(body, opts = {})
196
+ comments = opts[:comments]
197
+ items = body.split("\n--\n")
198
+ list = []
199
+ items.each do |item|
200
+ # Yes, this messes with the "content:" field but that's the one that's upsetting Mail.new
201
+ item.gsub!(/\n\s*\n/, "\n") # remove blank lines for Mail
202
+ history = Mail.new(item)
203
+ next if not comments and history['type'].to_s =~ /Comment/ # skip comments
204
+ reply = {}
205
+
206
+ history.header.fields.each_with_index do |header, index|
207
+ next if index == 0
208
+
209
+ key = header.name.to_s.downcase
210
+ value = header.value.to_s
211
+
212
+ attachments = []
213
+ case key
214
+ when 'attachments'
215
+ temp = item.match(/Attachments:\s*(.*)/m)
216
+ if temp.class != NilClass
217
+ atarr = temp[1].split("\n")
218
+ atarr.map { |a| a.gsub!(/^\s*/, '') }
219
+ atarr.each do |a|
220
+ i = a.match(/(\d+):\s*(.*)/)
221
+ s = {
222
+ :id => i[1].to_s,
223
+ :name => i[2].to_s
224
+ }
225
+ sz = i[2].match(/(.*?)\s*\((.*?)\)/)
226
+ if sz.class == MatchData
227
+ s[:name] = sz[1].to_s
228
+ s[:size] = sz[2].to_s
229
+ end
230
+ attachments << s
231
+ end
232
+ reply['attachments'] = attachments
233
+ end
234
+ when 'content'
235
+ reply['content'] = value
236
+ else
237
+ reply["#{key}"] = value
238
+ end
239
+ end
240
+ list << reply
241
+ end
242
+
243
+ list
244
+ end
245
+ end
data/lib/roust/user.rb ADDED
@@ -0,0 +1,48 @@
1
+ module Roust::User
2
+ # id can be numeric (e.g. 28) or textual (e.g. john)
3
+ def user_show(id)
4
+ response = self.class.get("/user/#{id}")
5
+
6
+ body, _ = explode_response(response)
7
+ if body =~ /No user named/
8
+ nil
9
+ else
10
+ body.gsub!(/\n\s*\n/, "\n") # remove blank lines for Mail
11
+ message = Mail.new(body)
12
+ Hash[message.header.fields.map { |header|
13
+ key = header.name.to_s.downcase
14
+ value = header.value.to_s
15
+ [ key, value ]
16
+ }]
17
+ end
18
+ end
19
+
20
+ def user_update(id, attrs)
21
+ content = compose_content('user', id, attrs)
22
+
23
+ response = self.class.post(
24
+ "/user/#{id}/edit",
25
+ :body => {
26
+ :content => content
27
+ }
28
+ )
29
+
30
+ body, _ = explode_response(response)
31
+
32
+ case body
33
+ when /^# User (.+) updated/
34
+ id = body[/^# User (.+) updated/, 1]
35
+ user_show(id)
36
+ when /^# You are not allowed to modify user \d+/
37
+ raise Unauthorized, body
38
+ when /^# Syntax error/
39
+ raise SyntaxError, body
40
+ else
41
+ raise UnhandledResponse
42
+ end
43
+ end
44
+
45
+ # TODO(auxesis): add method for creating a user
46
+
47
+ alias_method :user, :user_show
48
+ end
data/lib/roust/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Roust
2
- VERSION = '1.3.0'
2
+ VERSION = '1.4.0'
3
3
  end