cherby 0.0.3 → 0.0.4
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.
- data/cherby.gemspec +1 -1
- data/lib/cherby/business_object.rb +60 -20
- data/lib/cherby/exceptions.rb +3 -0
- data/lib/cherby/incident.rb +3 -14
- data/lib/cherby/journal_note.rb +0 -4
- data/lib/cherby/task.rb +3 -5
- data/spec/business_object_spec.rb +38 -20
- data/spec/cherwell_spec.rb +36 -0
- metadata +2 -2
data/cherby.gemspec
CHANGED
@@ -3,22 +3,27 @@ require 'nokogiri'
|
|
3
3
|
module Cherby
|
4
4
|
# Cherwell BusinessObject wrapper, with data represented as an XML DOM
|
5
5
|
class BusinessObject
|
6
|
+
# Return the name of this class.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
def self.object_type
|
11
|
+
return self.name.split('::').last
|
12
|
+
end
|
6
13
|
|
7
|
-
#
|
8
|
-
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
attr_accessor :object_name, :default_values
|
14
|
+
# Return the name of this instance's class.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
#
|
18
|
+
def object_type
|
19
|
+
return self.class.object_type
|
14
20
|
end
|
15
21
|
|
16
22
|
# Create a new BusinessObject subclass instance from the given hash of
|
17
23
|
# 'FieldName' => 'Field Value'.
|
18
24
|
def self.create(fields={})
|
19
|
-
type_name = self.object_name
|
20
25
|
builder = Nokogiri::XML::Builder.new {
|
21
|
-
BusinessObject_('Name' =>
|
26
|
+
BusinessObject_('Name' => self.object_type, 'RecID' => 'TODO') {
|
22
27
|
FieldList_ {
|
23
28
|
fields.each { |name, value|
|
24
29
|
Field_(value, 'Name' => name)
|
@@ -35,6 +40,24 @@ module Cherby
|
|
35
40
|
attr_reader :dom
|
36
41
|
|
37
42
|
# Create a new BusinessObject instance from the given XML string.
|
43
|
+
#
|
44
|
+
# @param [String] xml
|
45
|
+
# XML for the BusinessObject to create. The XML must contain a
|
46
|
+
# `BusinessObject` element, with a `FieldList` element within it,
|
47
|
+
# containing zero or more `Field` elements, each having a `Name`
|
48
|
+
# attribute, and a string value. For example:
|
49
|
+
#
|
50
|
+
# <BusinessObject Name="Customer">
|
51
|
+
# <FieldList>
|
52
|
+
# <Field Name="FirstName">Steven</Field>
|
53
|
+
# <Field Name="LastName">Moffat</Field>
|
54
|
+
# [...]
|
55
|
+
# </FieldList>
|
56
|
+
# </BusinessObject>
|
57
|
+
#
|
58
|
+
# @raise [Cherby::BadFormat]
|
59
|
+
# If the XML DOM's structure is incorrect
|
60
|
+
#
|
38
61
|
def initialize(xml)
|
39
62
|
@dom = Nokogiri::XML(xml)
|
40
63
|
check_dom_format!
|
@@ -57,7 +80,11 @@ module Cherby
|
|
57
80
|
return true
|
58
81
|
end
|
59
82
|
|
60
|
-
# Return the XML representation of this BusinessObject
|
83
|
+
# Return the XML representation of this BusinessObject.
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
# The BusinessObject's XML representation
|
87
|
+
#
|
61
88
|
def to_xml
|
62
89
|
return @dom.to_xml
|
63
90
|
end
|
@@ -71,6 +98,9 @@ module Cherby
|
|
71
98
|
# The node for the `Field` element, or `nil` if no Field
|
72
99
|
# with the given `Name` exists.
|
73
100
|
#
|
101
|
+
# @raise [Cherby::BadFormat]
|
102
|
+
# If the XML DOM's structure is incorrect
|
103
|
+
#
|
74
104
|
def get_field_node(field_name)
|
75
105
|
check_dom_format!
|
76
106
|
selector = "BusinessObject > FieldList > Field[@Name=#{field_name}]"
|
@@ -119,12 +149,12 @@ module Cherby
|
|
119
149
|
# The date/time string to parse. May or may not include a trailing
|
120
150
|
# [+-]HH:MM or [+-]HHMM.
|
121
151
|
#
|
122
|
-
# @param [Integer]
|
152
|
+
# @param [Integer] default_tz_offset
|
123
153
|
# Offset in hours (positive or negative) between UTC and the given
|
124
|
-
# `dt_string`. For example, Eastern Time is `-5`. This is ONLY used if
|
154
|
+
# `dt_string`. For example, US Eastern Time is `-5`. This is ONLY used if
|
125
155
|
# `dt_string` does NOT include a trailing offset component.
|
126
156
|
#
|
127
|
-
def self.parse_datetime(dt_string,
|
157
|
+
def self.parse_datetime(dt_string, default_tz_offset=0)
|
128
158
|
begin
|
129
159
|
result = DateTime.parse(dt_string)
|
130
160
|
rescue
|
@@ -133,27 +163,37 @@ module Cherby
|
|
133
163
|
# If offset was part of the dt_string, use new_offset to get UTC
|
134
164
|
if dt_string =~ /[+-]\d\d:?\d\d$/
|
135
165
|
return result.new_offset(0)
|
136
|
-
# Otherwise, subtract the
|
166
|
+
# Otherwise, subtract the default offset to get UTC time
|
137
167
|
else
|
138
|
-
return result - Rational(
|
168
|
+
return result - Rational(default_tz_offset.to_i, 24)
|
139
169
|
end
|
140
170
|
end
|
141
171
|
|
142
172
|
# Return the last-modified date/time of this BusinessObject
|
143
|
-
# (LastModDateTime converted to DateTime)
|
144
|
-
|
173
|
+
# (LastModDateTime converted to DateTime).
|
174
|
+
#
|
175
|
+
# @param [Integer] default_tz_offset
|
176
|
+
# Offset in hours (positive or negative) between UTC and the given
|
177
|
+
# `dt_string`. For example, US Eastern Time is `-5`. This is ONLY used if
|
178
|
+
# `dt_string` does NOT include a trailing offset component.
|
179
|
+
#
|
180
|
+
# @raise [Cherby::MissingData, Cherby::BadFormat]
|
181
|
+
# If `LastModDateTime` is missing or cannot be parsed, respectively
|
182
|
+
#
|
183
|
+
def modified(default_tz_offset=0)
|
145
184
|
last_mod = self['LastModDateTime']
|
146
185
|
if last_mod.nil? || last_mod.empty?
|
147
|
-
raise
|
186
|
+
raise Cherby::MissingData.new("BusinessObject is missing LastModDateTime field.")
|
148
187
|
end
|
149
188
|
begin
|
150
|
-
return BusinessObject.parse_datetime(last_mod)
|
189
|
+
return BusinessObject.parse_datetime(last_mod, default_tz_offset)
|
151
190
|
rescue(ArgumentError)
|
152
|
-
raise
|
191
|
+
raise Cherby::BadFormat.new("Cannot parse LastModDateTime: '#{last_mod}'")
|
153
192
|
end
|
154
193
|
end
|
155
194
|
|
156
195
|
# Return the last-modified time as a human-readable string
|
196
|
+
# TODO: Make this more error-tolerant with a default date?
|
157
197
|
def mod_s
|
158
198
|
return modified.strftime('%Y-%m-%d %H:%M:%S')
|
159
199
|
end
|
data/lib/cherby/exceptions.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Cherby
|
2
|
+
# Base class for all Cherby custom exceptions
|
2
3
|
class CherbyError < RuntimeError; end
|
4
|
+
|
3
5
|
class LoginFailed < CherbyError; end
|
4
6
|
class NotFound < CherbyError; end
|
7
|
+
class MissingData < CherbyError; end
|
5
8
|
class BadFormat < CherbyError; end
|
6
9
|
|
7
10
|
class SoapError < CherbyError
|
data/lib/cherby/incident.rb
CHANGED
@@ -6,18 +6,7 @@ require 'cherby/journal_note'
|
|
6
6
|
module Cherby
|
7
7
|
# Wrapper for Cherwell incident objects.
|
8
8
|
class Incident < BusinessObject
|
9
|
-
|
10
|
-
# FIXME: Rename these and make use of them?
|
11
|
-
@default_values = {
|
12
|
-
:service => "Auto Generated",
|
13
|
-
:service_group => "Auto Generated",
|
14
|
-
:category => "Auto Generated",
|
15
|
-
:sub_category => "JIRA",
|
16
|
-
:impact => "Inconvenience",
|
17
|
-
:urgency => "Medium",
|
18
|
-
:priority => "3",
|
19
|
-
}
|
20
|
-
|
9
|
+
# Return the Incident's public ID
|
21
10
|
def id
|
22
11
|
self['IncidentID']
|
23
12
|
end
|
@@ -46,7 +35,7 @@ module Cherby
|
|
46
35
|
self["SubcategoryNonHR"] = "JIRA"
|
47
36
|
end
|
48
37
|
|
49
|
-
# Return Task instances for all tasks associated with this Incident
|
38
|
+
# Return Task instances for all tasks associated with this Incident.
|
50
39
|
#
|
51
40
|
# @return [Array<Task>]
|
52
41
|
#
|
@@ -56,7 +45,7 @@ module Cherby
|
|
56
45
|
end
|
57
46
|
end
|
58
47
|
|
59
|
-
# Return all
|
48
|
+
# Return all JournalNotes associated with this Incident.
|
60
49
|
#
|
61
50
|
# @return [Array<JournalNote>]
|
62
51
|
#
|
data/lib/cherby/journal_note.rb
CHANGED
data/lib/cherby/task.rb
CHANGED
@@ -2,16 +2,14 @@ require 'date'
|
|
2
2
|
require 'cherby/business_object'
|
3
3
|
|
4
4
|
module Cherby
|
5
|
+
# Wrapper for Cherwell task objects.
|
5
6
|
class Task < BusinessObject
|
6
|
-
|
7
|
-
@default_values = {
|
8
|
-
:status => "New",
|
9
|
-
}
|
10
|
-
|
7
|
+
# Return this task's public ID.
|
11
8
|
def id
|
12
9
|
self['TaskID']
|
13
10
|
end
|
14
11
|
|
12
|
+
# Return true if this task exists in Cherwell.
|
15
13
|
def exists?
|
16
14
|
return !id.to_s.empty?
|
17
15
|
end
|
@@ -1,25 +1,24 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
2
|
require 'cherby/business_object'
|
3
|
+
require 'cherby/exceptions'
|
3
4
|
|
4
5
|
# Some BusinessObject subclasses to test with
|
5
|
-
class MySubclass < Cherby::BusinessObject
|
6
|
-
@object_name = 'MySubclass'
|
7
|
-
@default_values = {}
|
8
|
-
end
|
9
|
-
|
10
|
-
class MySubclassNoTemplate < Cherby::BusinessObject
|
11
|
-
@object_name = 'MySubclassNoTemplate'
|
12
|
-
@default_values = {}
|
13
|
-
end
|
14
|
-
|
15
|
-
class MySubclassNoTemplateFile < Cherby::BusinessObject
|
16
|
-
@object_name = 'MySubclassNoTemplateFile'
|
17
|
-
@default_values = {}
|
18
|
-
end
|
6
|
+
class MySubclass < Cherby::BusinessObject; end
|
19
7
|
|
20
8
|
describe Cherby::BusinessObject do
|
21
9
|
context "Class methods" do
|
10
|
+
describe "#object_type" do
|
11
|
+
it "returns the plain class name as a string" do
|
12
|
+
MySubclass.object_type.should == 'MySubclass'
|
13
|
+
end
|
14
|
+
end #object_type
|
15
|
+
|
22
16
|
describe "#create" do
|
17
|
+
it "uses object_type as the BusinessObject `Name` attribute" do
|
18
|
+
obj = MySubclass.create({})
|
19
|
+
obj.dom.css("BusinessObject").first.attr('Name').should == 'MySubclass'
|
20
|
+
end
|
21
|
+
|
23
22
|
it "sets options in the DOM" do
|
24
23
|
obj = MySubclass.create({'First' => 'Eric', 'Last' => 'Idle'})
|
25
24
|
first_name = obj.dom.css("BusinessObject[@Name=MySubclass] Field[@Name=First]").first
|
@@ -58,6 +57,13 @@ describe Cherby::BusinessObject do
|
|
58
57
|
|
59
58
|
|
60
59
|
context "Instance methods" do
|
60
|
+
describe "#object_type" do
|
61
|
+
it "returns the plain class name as a string" do
|
62
|
+
obj = MySubclass.create({})
|
63
|
+
obj.object_type.should == 'MySubclass'
|
64
|
+
end
|
65
|
+
end #object_type
|
66
|
+
|
61
67
|
describe "#initialize" do
|
62
68
|
it "accepts an XML string" do
|
63
69
|
xml = %Q{
|
@@ -75,7 +81,7 @@ describe Cherby::BusinessObject do
|
|
75
81
|
last_name.content.should == "Idle"
|
76
82
|
end
|
77
83
|
|
78
|
-
it "raises BadFormat if XML is missing FieldList" do
|
84
|
+
it "raises Cherby::BadFormat if XML is missing FieldList" do
|
79
85
|
xml = %Q{
|
80
86
|
<BusinessObject Name="MySubclass">
|
81
87
|
<Whatever/>
|
@@ -87,7 +93,7 @@ describe Cherby::BusinessObject do
|
|
87
93
|
Cherby::BadFormat, /missing 'BusinessObject > FieldList'/)
|
88
94
|
end
|
89
95
|
|
90
|
-
it "raises BadFormat if XML is missing BusinessObject" do
|
96
|
+
it "raises Cherby::BadFormat if XML is missing BusinessObject" do
|
91
97
|
xml = %Q{
|
92
98
|
<Whatever Name="MySubclass"/>
|
93
99
|
}
|
@@ -132,7 +138,7 @@ describe Cherby::BusinessObject do
|
|
132
138
|
end #[]=
|
133
139
|
|
134
140
|
describe "#modified" do
|
135
|
-
it "
|
141
|
+
it "returns the parsed LastModDateTime if it's a valid date/time string" do
|
136
142
|
# Parse the string version of now in order to truncate extra precision
|
137
143
|
# (otherwise the date won't compare equal later)
|
138
144
|
now = DateTime.parse(DateTime.now.to_s)
|
@@ -141,14 +147,26 @@ describe Cherby::BusinessObject do
|
|
141
147
|
obj.modified.should == now
|
142
148
|
end
|
143
149
|
|
144
|
-
it "
|
150
|
+
it "adjusts by default_tz_offset if LastModDateTime does not include an offset" do
|
151
|
+
# Current time, without timezone offset
|
152
|
+
now_str = DateTime.now.strftime('%Y-%m-%dT%H:%M:%S')
|
153
|
+
now = DateTime.parse(now_str)
|
154
|
+
|
155
|
+
obj = MySubclass.create({'LastModDateTime' => now_str})
|
156
|
+
# Test a bunch of different timezone offsets
|
157
|
+
[-7, -5, -2, 0, 1, 3, 4].each do |offset|
|
158
|
+
obj.modified(offset).should == now - Rational(offset, 24)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it "raises Cherby::BadFormat if LastModDateTime value is invalid" do
|
145
163
|
obj = MySubclass.create({'LastModDateTime' => 'bogus'})
|
146
164
|
lambda do
|
147
165
|
obj.modified
|
148
|
-
end.should raise_error(
|
166
|
+
end.should raise_error(Cherby::BadFormat, /Cannot parse LastModDateTime: 'bogus'/)
|
149
167
|
end
|
150
168
|
|
151
|
-
it "
|
169
|
+
it "raises Cherby::MissingData if LastModDateTime is empty" do
|
152
170
|
xml = %Q{
|
153
171
|
<BusinessObject Name="MySubclass">
|
154
172
|
<FieldList>
|
data/spec/cherwell_spec.rb
CHANGED
@@ -172,8 +172,44 @@ describe Cherby::Cherwell do
|
|
172
172
|
result = @cherwell.get_object_xml(@name, @public_id)
|
173
173
|
result.should == xml
|
174
174
|
end
|
175
|
+
|
176
|
+
it "raises Cherby::SoapError if Savon::SOAPFault occurs" do
|
177
|
+
soap_fault = Savon::SOAPFault.new('fake-http', 'fake-nori')
|
178
|
+
@cherwell.client.stub(:call_wrap).and_raise(soap_fault)
|
179
|
+
lambda do
|
180
|
+
@cherwell.get_object_xml(@name, @public_id)
|
181
|
+
end.should raise_error { |ex|
|
182
|
+
ex.should be_a(Cherby::SoapError)
|
183
|
+
ex.message.should =~ /SOAPFault from method/
|
184
|
+
ex.http.should == 'fake-http'
|
185
|
+
}
|
186
|
+
end
|
175
187
|
end #get_object_xml
|
176
188
|
|
189
|
+
describe "#get_business_object" do
|
190
|
+
before(:each) do
|
191
|
+
@name = 'Thing'
|
192
|
+
@rec_id = '12345678901234567890123456789012'
|
193
|
+
@public_id = '12345'
|
194
|
+
@business_object_xml = %Q{
|
195
|
+
<BusinessObject>
|
196
|
+
<FieldList>
|
197
|
+
<Field Name="Name">#{@name}</Field>
|
198
|
+
<Field Name="RecID">#{@rec_id}</Field>
|
199
|
+
</FieldList>
|
200
|
+
</BusinessObject>
|
201
|
+
}
|
202
|
+
@cherwell.stub(:get_object_xml).
|
203
|
+
with(@name, @public_id).
|
204
|
+
and_return(@business_object_xml)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "returns a BusinessObject instance" do
|
208
|
+
result = @cherwell.get_business_object(@name, @public_id)
|
209
|
+
result.should be_a(Cherby::BusinessObject)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
177
213
|
describe "#update_object_xml" do
|
178
214
|
before(:each) do
|
179
215
|
@public_id = '80909'
|
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.
|
4
|
+
version: 0.0.4
|
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-
|
12
|
+
date: 2014-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httpclient
|