cherby 0.0.2 → 0.0.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.
@@ -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>