cherby 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cherby"
3
- s.version = "0.0.2"
3
+ s.version = "0.0.3"
4
4
  s.summary = "Cherwell-Ruby bridge"
5
5
  s.description = <<-EOS
6
6
  Cherby is a Ruby wrapper for the Cherwell Web Service.
@@ -14,7 +14,6 @@ Gem::Specification.new do |s|
14
14
  s.add_dependency 'savon', '>= 2.3.0'
15
15
  s.add_dependency 'yajl-ruby'
16
16
  s.add_dependency 'nokogiri'
17
- s.add_dependency 'mustache'
18
17
 
19
18
  s.add_development_dependency "rake"
20
19
  s.add_development_dependency "simplecov"
@@ -34,9 +34,27 @@ module Cherby
34
34
 
35
35
  attr_reader :dom
36
36
 
37
- # Create a new instance populated with the given XML string
37
+ # Create a new BusinessObject instance from the given XML string.
38
38
  def initialize(xml)
39
39
  @dom = Nokogiri::XML(xml)
40
+ check_dom_format!
41
+ end
42
+
43
+ # Ensure the @dom is in the expected format for BusinessObjects.
44
+ #
45
+ # @raise [Cherby::BadFormat]
46
+ # If the DOM's structure is incorrect
47
+ #
48
+ def check_dom_format!
49
+ # Ensure XML is in expected format for BusinessObjects
50
+ if @dom.css('BusinessObject').empty?
51
+ raise Cherby::BadFormat.new(
52
+ "BusinessObject XML is missing 'BusinessObject' element")
53
+ elsif @dom.css('BusinessObject > FieldList').empty?
54
+ raise Cherby::BadFormat.new(
55
+ "BusinessObject XML is missing 'BusinessObject > FieldList' element")
56
+ end
57
+ return true
40
58
  end
41
59
 
42
60
  # Return the XML representation of this BusinessObject
@@ -45,9 +63,30 @@ module Cherby
45
63
  end
46
64
 
47
65
  # Return the node of the field with the given name.
66
+ #
67
+ # @param [String] field_name
68
+ # The `Name` attribute of the `Field` element to get.
69
+ #
70
+ # @return [Nokogiri::XML::Node, nil]
71
+ # The node for the `Field` element, or `nil` if no Field
72
+ # with the given `Name` exists.
73
+ #
48
74
  def get_field_node(field_name)
75
+ check_dom_format!
49
76
  selector = "BusinessObject > FieldList > Field[@Name=#{field_name}]"
50
- element = @dom.css(selector).first
77
+ return @dom.css(selector).first
78
+ end
79
+
80
+ # Get a `Field` node with the given name, or create one if it doesn't exist.
81
+ #
82
+ # @param [String] field_name
83
+ # The `Name` attribute of the `Field` element to get or create.
84
+ #
85
+ # @return [Nokogiri::XML::Node]
86
+ # The node for the existing or new `Field` element.
87
+ #
88
+ def get_or_create_field_node(field_name)
89
+ element = get_field_node(field_name)
51
90
  if element.nil?
52
91
  element = Nokogiri::XML::Node.new('Field', @dom)
53
92
  element['Name'] = field_name
@@ -126,19 +165,16 @@ module Cherby
126
165
  end
127
166
 
128
167
  # Return the content in the field with the given name.
129
- # TODO: Exception for unknown field name
130
168
  def [](field_name)
131
- if field = get_field_node(field_name)
132
- return field.content
133
- end
169
+ field = get_field_node(field_name)
170
+ return field.content if field
171
+ return nil
134
172
  end
135
173
 
136
174
  # Modify the content in the field with the given name.
137
- # TODO: Exception for unknown field name
138
175
  def []=(field_name, value)
139
- if field = get_field_node(field_name)
140
- field.content = value.to_s
141
- end
176
+ field = get_or_create_field_node(field_name)
177
+ field.content = value.to_s
142
178
  end
143
179
 
144
180
  # Copy designated fields from one BusinessObject to another.
@@ -41,7 +41,7 @@ module Cherby
41
41
  # @return [Boolean]
42
42
  # `true` if login was successful
43
43
  #
44
- # @raise [LoginFailed]
44
+ # @raise [Cherby::LoginFailed]
45
45
  # If login failed for any reason
46
46
  #
47
47
  def login(username=nil, password=nil)
@@ -53,7 +53,7 @@ module Cherby
53
53
  response = @client.call(:login, :message => creds)
54
54
  rescue => e
55
55
  # This can happen if a bad URL is given
56
- raise LoginFailed, e.message
56
+ raise Cherby::LoginFailed, e.message
57
57
  else
58
58
  if response.body[:login_response][:login_result] == true
59
59
  # FIXME: Using the workaround described in this issue:
@@ -66,7 +66,7 @@ module Cherby
66
66
  return true
67
67
  # This can happen if invalid credentials are given
68
68
  else
69
- raise LoginFailed, "Cherwell returned false status"
69
+ raise Cherby::LoginFailed, "Cherwell returned false status"
70
70
  end
71
71
  end
72
72
  end
@@ -85,9 +85,18 @@ module Cherby
85
85
  #
86
86
  # @return [Incident]
87
87
  #
88
+ # @raise [Cherby::NotFound]
89
+ # If an error occurs when fetching the incident.
90
+ # The `message` attribute includes the error from Cherwell.
91
+ #
88
92
  def incident(id)
89
93
  incident_xml = get_object_xml('Incident', id)
90
- return Incident.new(incident_xml.to_s)
94
+ error = self.last_error
95
+ if error
96
+ raise Cherby::NotFound.new("Cannot find Incident '#{id}': #{error}")
97
+ else
98
+ return Incident.new(incident_xml.to_s)
99
+ end
91
100
  end
92
101
 
93
102
  # Get the Cherwell task with the given public ID, and return a Task
@@ -95,9 +104,18 @@ module Cherby
95
104
  #
96
105
  # @return [Task]
97
106
  #
107
+ # @raise [Cherby::NotFound]
108
+ # If an error occurs when fetching the task.
109
+ # The `message` attribute includes the error from Cherwell.
110
+ #
98
111
  def task(id)
99
112
  task_xml = get_object_xml('Task', id)
100
- return Task.new(task_xml.to_s)
113
+ error = self.last_error
114
+ if error
115
+ raise Cherby::NotFound.new("Cannot find Task '#{id}': #{error}")
116
+ else
117
+ return Task.new(task_xml.to_s)
118
+ end
101
119
  end
102
120
 
103
121
  # Get a business object based on its public ID or RecID, and return the
@@ -127,6 +145,9 @@ module Cherby
127
145
  # @return [String]
128
146
  # Raw XML response string.
129
147
  #
148
+ # @raise [Cherby::SoapError]
149
+ # If a Savon::SOAPFault occurs when making the request.
150
+ #
130
151
  def get_object_xml(object_type, id)
131
152
  # Assemble the SOAP body
132
153
  body = {:busObNameOrId => object_type}
@@ -143,13 +164,19 @@ module Cherby
143
164
 
144
165
  begin
145
166
  result = @client.call_wrap(method, body)
146
- rescue Savon::Error => e
147
- raise SoapError, e.message
167
+ rescue Savon::SOAPFault => e
168
+ raise Cherby::SoapError.new(
169
+ "SOAPFault from method '#{method}'", e.http)
148
170
  else
149
- return result
171
+ return result.to_s
150
172
  end
151
173
  end
152
174
 
175
+ # Get a BusinessObject instance
176
+ def get_business_object(object_type, id)
177
+ xml = self.get_object_xml(object_type, id)
178
+ return BusinessObject.new(xml)
179
+ end
153
180
 
154
181
  # Update a given Cherwell object by submitting its XML to the SOAP
155
182
  # interface.
@@ -1,6 +1,15 @@
1
1
  module Cherby
2
2
  class CherbyError < RuntimeError; end
3
3
  class LoginFailed < CherbyError; end
4
- class SoapError < CherbyError; end
4
+ class NotFound < CherbyError; end
5
+ class BadFormat < CherbyError; end
6
+
7
+ class SoapError < CherbyError
8
+ attr_reader :http
9
+ def initialize(message, http=nil)
10
+ super(message)
11
+ @http = http
12
+ end
13
+ end
5
14
  end # module Cherby
6
15
 
@@ -7,6 +7,7 @@ module Cherby
7
7
  # Wrapper for Cherwell incident objects.
8
8
  class Incident < BusinessObject
9
9
  @object_name = 'Incident'
10
+ # FIXME: Rename these and make use of them?
10
11
  @default_values = {
11
12
  :service => "Auto Generated",
12
13
  :service_group => "Auto Generated",
@@ -75,14 +75,44 @@ describe Cherby::BusinessObject do
75
75
  last_name.content.should == "Idle"
76
76
  end
77
77
 
78
+ it "raises BadFormat if XML is missing FieldList" do
79
+ xml = %Q{
80
+ <BusinessObject Name="MySubclass">
81
+ <Whatever/>
82
+ </BusinessObject>
83
+ }
84
+ lambda do
85
+ @obj = MySubclass.new(xml)
86
+ end.should raise_error(
87
+ Cherby::BadFormat, /missing 'BusinessObject > FieldList'/)
88
+ end
89
+
90
+ it "raises BadFormat if XML is missing BusinessObject" do
91
+ xml = %Q{
92
+ <Whatever Name="MySubclass"/>
93
+ }
94
+ lambda do
95
+ @obj = MySubclass.new(xml)
96
+ end.should raise_error(
97
+ Cherby::BadFormat, /missing 'BusinessObject'/)
98
+ end
78
99
  end #initialize
79
100
 
101
+ describe "#check_dom_format!" do
102
+ it "TODO"
103
+ end #check_dom_format!
104
+
80
105
  describe "#[]" do
81
106
  it "returns the value in the named field" do
82
107
  obj = MySubclass.create({'First' => 'Eric', 'Last' => 'Idle'})
83
108
  obj['First'].should == 'Eric'
84
109
  obj['Last'].should == 'Idle'
85
110
  end
111
+
112
+ it "returns nil if the field doesn't exist" do
113
+ obj = MySubclass.create({'First' => 'Eric', 'Last' => 'Idle'})
114
+ obj['Middle'].should be_nil
115
+ end
86
116
  end #[]
87
117
 
88
118
  describe "#[]=" do
@@ -169,6 +199,11 @@ describe Cherby::BusinessObject do
169
199
  @obj = MySubclass.new(xml)
170
200
  end
171
201
 
202
+ it "checks the DOM format" do
203
+ @obj.should_receive(:check_dom_format!).and_return(true)
204
+ @obj.get_field_node('First')
205
+ end
206
+
172
207
  it "returns the Field having the given Name" do
173
208
  first_node = @obj.get_field_node('First')
174
209
  first_node.should be_a(Nokogiri::XML::Node)
@@ -176,13 +211,44 @@ describe Cherby::BusinessObject do
176
211
  first_node.inner_html.should == 'Eric'
177
212
  end
178
213
 
179
- it "creates a new Field if it doesn't exist" do
214
+ it "returns nil if the field doesn't exist" do
180
215
  middle_node = @obj.get_field_node('Middle')
216
+ middle_node.should be_nil
217
+ end
218
+ end #get_field_node
219
+
220
+ describe "#get_or_create_field_node" do
221
+ before(:each) do
222
+ xml = %Q{
223
+ <BusinessObject Name="MySubclass">
224
+ <FieldList>
225
+ <Field Name="First">Eric</Field>
226
+ <Field Name="Last">Idle</Field>
227
+ </FieldList>
228
+ </BusinessObject>
229
+ }
230
+ @obj = MySubclass.new(xml)
231
+ end
232
+
233
+ it "checks the DOM format" do
234
+ @obj.should_receive(:check_dom_format!).and_return(true)
235
+ @obj.get_or_create_field_node('First')
236
+ end
237
+
238
+ it "returns the Field having the given Name" do
239
+ first_node = @obj.get_or_create_field_node('First')
240
+ first_node.should be_a(Nokogiri::XML::Node)
241
+ first_node.attr('Name').should == 'First'
242
+ first_node.inner_html.should == 'Eric'
243
+ end
244
+
245
+ it "creates a new Field if it doesn't exist" do
246
+ middle_node = @obj.get_or_create_field_node('Middle')
181
247
  middle_node.should be_a(Nokogiri::XML::Node)
182
248
  middle_node.attr('Name').should == 'Middle'
183
249
  middle_node.inner_html.should == ''
184
250
  end
185
- end #get_field_node
251
+ end #get_or_create_field_node
186
252
 
187
253
  describe "#field_values" do
188
254
  before(:each) do
@@ -77,22 +77,52 @@ describe Cherby::Cherwell do
77
77
  it "returns a Cherby::Incident instance" do
78
78
  incident_id = '51949'
79
79
  incident_xml = File.read(File.join(DATA_DIR, 'incident.xml'))
80
+ @cherwell.stub(:last_error => nil)
80
81
  @cherwell.stub(:get_object_xml).
81
82
  with('Incident', incident_id).
82
83
  and_return(incident_xml)
83
84
  @cherwell.incident(incident_id).should be_a(Cherby::Incident)
84
85
  end
86
+
87
+ it "raises Cherby::NotFound if incident is not found" do
88
+ incident_id = '99999'
89
+ empty_xml = File.read(File.join(DATA_DIR, 'empty.xml'))
90
+ @cherwell.stub(:get_object_xml).
91
+ with('Incident', incident_id).
92
+ and_return(empty_xml)
93
+
94
+ error = "Specified Business Object not found"
95
+ @cherwell.stub(:last_error => error)
96
+ lambda do
97
+ @cherwell.incident(incident_id)
98
+ end.should raise_error(Cherby::NotFound, /#{error}/)
99
+ end
85
100
  end #incident
86
101
 
87
102
  describe "#task" do
88
103
  it "returns a Cherby::Task instance" do
89
104
  task_id = '12345'
90
105
  task_xml = File.read(File.join(DATA_DIR, 'task.xml'))
106
+ @cherwell.stub(:last_error => nil)
91
107
  @cherwell.stub(:get_object_xml).
92
108
  with('Task', task_id).
93
109
  and_return(task_xml)
94
110
  @cherwell.task(task_id).should be_a(Cherby::Task)
95
111
  end
112
+
113
+ it "raises a Cherby::NotFound if task is not found" do
114
+ task_id = '99999'
115
+ empty_xml = File.read(File.join(DATA_DIR, 'empty.xml'))
116
+ @cherwell.stub(:get_object_xml).
117
+ with('Task', task_id).
118
+ and_return(empty_xml)
119
+
120
+ error = "Specified Business Object not found"
121
+ @cherwell.stub(:last_error => error)
122
+ lambda do
123
+ @cherwell.task(task_id)
124
+ end.should raise_error(Cherby::NotFound, /#{error}/)
125
+ end
96
126
  end #task
97
127
 
98
128
  describe "#get_object_xml" do
@@ -142,15 +172,6 @@ describe Cherby::Cherwell do
142
172
  result = @cherwell.get_object_xml(@name, @public_id)
143
173
  result.should == xml
144
174
  end
145
-
146
- it "raises a Cherby::SoapError if a Savon::Error occurs" do
147
- soap_fault = Savon::Error.new("Kaboom")
148
- @cherwell.client.stub(:call).and_raise(soap_fault)
149
- lambda do
150
- @cherwell.get_object_xml(@name, @public_id)
151
- end.should raise_error(
152
- Cherby::SoapError, /Kaboom/)
153
- end
154
175
  end #get_object_xml
155
176
 
156
177
  describe "#update_object_xml" do
@@ -0,0 +1 @@
1
+ <?xml version="1.0"?>
@@ -3,7 +3,6 @@ require 'simplecov'
3
3
  SimpleCov.start if ENV['COVERAGE']
4
4
 
5
5
  require 'rspec'
6
- require 'mustache'
7
6
 
8
7
  ROOT_DIR = File.absolute_path(File.join(File.dirname(__FILE__), '..'))
9
8
  LIB_DIR = File.join(ROOT_DIR, 'lib')
@@ -14,19 +13,23 @@ CONFIG_FILE = File.join(ROOT_DIR, 'config', 'test-config.yml')
14
13
  CHERWELL_WSDL = File.join(DATA_DIR, 'cherwell.wsdl')
15
14
  $LOAD_PATH.unshift(LIB_DIR)
16
15
 
17
- class XMLTemplate < Mustache
18
- self.template_path = XML_DIR
19
- end
20
-
21
16
  RSpec.configure do |config|
22
- def xml_envelope(body)
23
- return XMLTemplate.render_file('soap_envelope', :body => body)
24
- end
25
-
26
17
  def xml_response(method, body)
27
- wrapped_body = XMLTemplate.render_file(
28
- 'method_response_result', :method => method, :body => body)
29
- return xml_envelope(wrapped_body)
18
+ env_attrs = {
19
+ 'xmlns:soap' => "http://schemas.xmlsoap.org/soap/envelope/",
20
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
21
+ 'xmlns:xsd' => "http://www.w3.org/2001/XMLSchema",
22
+ }
23
+ builder = Nokogiri::XML::Builder.new do |xml|
24
+ xml['soap'].Envelope(env_attrs) do |env|
25
+ env.Body do
26
+ env.send(:"#{method}Response", 'xmlns' => 'http://cherwellsoftware.com') do
27
+ env.send(:"#{method}Result", body)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ return builder.to_xml
30
33
  end
31
34
 
32
35
  def savon_response(method, body)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cherby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-25 00:00:00.000000000 Z
12
+ date: 2014-03-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httpclient
@@ -75,22 +75,6 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: mustache
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :runtime
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
78
  - !ruby/object:Gem::Dependency
95
79
  name: rake
96
80
  requirement: !ruby/object:Gem::Requirement
@@ -215,6 +199,7 @@ files:
215
199
  - spec/cherwell_spec.rb
216
200
  - spec/client_spec.rb
217
201
  - spec/data/cherwell.wsdl
202
+ - spec/data/empty.xml
218
203
  - spec/data/incident.xml
219
204
  - spec/data/login_false.xml
220
205
  - spec/data/login_true.xml
@@ -223,8 +208,6 @@ files:
223
208
  - spec/journal_note_spec.rb
224
209
  - spec/spec_helper.rb
225
210
  - spec/task_spec.rb
226
- - spec/xml/method_response_result.mustache
227
- - spec/xml/soap_envelope.mustache
228
211
  - tasks/pry.rake
229
212
  - tasks/spec.rake
230
213
  homepage: http://github.com/a-e/cherby
@@ -1,4 +0,0 @@
1
- <{{method}}Response xmlns="http://cherwellsoftware.com">
2
- <{{method}}Result>{{body}}</{{method}}Result>
3
- </{{method}}Response>
4
-
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
- <soap:Body>
4
- {{{body}}}
5
- </soap:Body>
6
- </soap:Envelope>