cherby 0.0.1

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.
@@ -0,0 +1,147 @@
1
+ require 'savon'
2
+
3
+ module Cherby
4
+ # Cherwell SOAP Client wrapper
5
+ class Client < Savon::Client
6
+
7
+ # Create a Cherwell Client for the SOAP API at the given base URL.
8
+ #
9
+ # @param [String] base_url
10
+ # Full URL to the Cherwell web service WSDL, typically something
11
+ # like "http://my.hostname.com/CherwellService/api.asmx?WSDL".
12
+ # The `http://` and `?WSDL` parts are automatically added if missing.
13
+ #
14
+ def initialize(base_url)
15
+ if File.exist?(base_url)
16
+ wsdl_url = base_url
17
+ elsif base_url =~ /^http/
18
+ if base_url.downcase.end_with?('?wsdl')
19
+ wsdl_url = base_url
20
+ else
21
+ wsdl_url = "#{base_url}?WSDL"
22
+ end
23
+ elsif base_url !~ /^http/
24
+ raise ArgumentError, "Client URL must be a local file, or begin with http"
25
+ end
26
+ super(:wsdl => wsdl_url)
27
+ end
28
+
29
+ # Call a given SOAP method with an optional body.
30
+ #
31
+ # @example
32
+ # client.call_wrap(:login, {:userId => 'garak', :pasword => 'fabric'})
33
+ # # => true
34
+ #
35
+ # @param [Symbol] method
36
+ # Cherwell API method name, as a `:snake_case_symbol`.
37
+ # Must be one of the methods returned by `#known_methods`.
38
+ # @param [Hash<String>] body
39
+ # Message body to pass to the method.
40
+ #
41
+ # @return [String]
42
+ # Content found inside the returned XML document's `<[MethodName]Result>`
43
+ # element.
44
+ #
45
+ def call_wrap(method, body={})
46
+ method = method.to_sym
47
+ if !known_methods.include?(method)
48
+ raise ArgumentError, "Unknown Cherwell SOAP API method: #{method}"
49
+ end
50
+ # FIXME: Let Savon handle this snake_case stuff
51
+ # Each request has a *_response containing a *_result
52
+ response_field = (method.to_s + '_response').to_sym
53
+ result_field = (method.to_s + '_result').to_sym
54
+ # Submit the request
55
+ response = self.call(method, :message => body)
56
+ return response.to_hash[response_field][result_field]
57
+ end
58
+
59
+ # If a method in `#known_methods` is called, send it as a request.
60
+ #
61
+ # @param [Symbol] method
62
+ # Cherwell API method name, as a `:snake_case_symbol`.
63
+ # Must be one of the methods returned by `#known_methods`.
64
+ #
65
+ # @param [String, Hash] args
66
+ # Positional arguments (strings) or a hash of arguments
67
+ # (`:symbol => 'String'`) to pass to the method.
68
+ #
69
+ # @example
70
+ # # Login with positional arguments:
71
+ # client.login('sisko', 'baseball')
72
+ # # or a Hash of arguments:
73
+ # client.login(:userId => 'sisko', :password => 'baseball')
74
+ #
75
+ # client.get_business_object_definition(
76
+ # :nameOrId => 'JournalNote')
77
+ #
78
+ def method_missing(method, *args, &block)
79
+ if known_methods.include?(method)
80
+ if args.first.is_a?(Hash)
81
+ call_wrap(method, args.first)
82
+ else
83
+ hash_args = args_to_hash(method, *args)
84
+ call_wrap(method, hash_args)
85
+ end
86
+ else
87
+ super
88
+ end
89
+ end
90
+
91
+ # Valid methods in the Cherwell SOAP API
92
+ #
93
+ # @return [Array<Symbol>]
94
+ #
95
+ def known_methods
96
+ return self.operations.sort
97
+ end
98
+
99
+ # Return parameters for the given Cherwell API method.
100
+ #
101
+ # @param [Symbol] method
102
+ # Cherwell API method name, as a `:snake_case_symbol`.
103
+ # Must be one of the methods returned by `#known_methods`.
104
+ #
105
+ # @return [Hash]
106
+ # Parameter definitions for the given method, or an empty
107
+ # hash if the method is unknown.
108
+ #
109
+ def params_for_method(method)
110
+ if @wsdl.operations.include?(method)
111
+ return @wsdl.operations[method][:parameters] || {}
112
+ else
113
+ return {}
114
+ end
115
+ end
116
+
117
+ # Convert positional parameters into a `:key => value` hash,
118
+ # with parameter names inferred from `#params_for_method`.
119
+ #
120
+ # @example
121
+ # client.args_to_hash(:login, 'odo', 'nerys')
122
+ # # => {:userId => 'odo', :password => 'nerys'}
123
+ #
124
+ # @param [Symbol] method
125
+ # Cherwell API method name, as a `:snake_case_symbol`.
126
+ # Must be one of the methods returned by `#known_methods`.
127
+ # @param [String] args
128
+ # Positional argument values
129
+ #
130
+ # @return [Hash<String>]
131
+ # `:key => value` for each positional argument.
132
+ #
133
+ # @raise [ArgumentError]
134
+ # If the given number of `args` doesn't match the number of parameters
135
+ # expected by `method`.
136
+ #
137
+ def args_to_hash(method, *args)
138
+ params = params_for_method(method)
139
+ if params.count != args.count
140
+ raise ArgumentError.new(
141
+ "Wrong number of arguments (#{args.count} for #{params.count})")
142
+ end
143
+ return Hash[params.keys.zip(args)]
144
+ end
145
+ end
146
+ end
147
+
@@ -0,0 +1,6 @@
1
+ module Cherby
2
+ class CherbyError < RuntimeError; end
3
+ class LoginFailed < CherbyError; end
4
+ class SoapError < CherbyError; end
5
+ end # module Cherby
6
+
@@ -0,0 +1,141 @@
1
+ require 'date'
2
+ require 'mustache'
3
+ require 'cherby/business_object'
4
+ require 'cherby/task'
5
+ require 'cherby/journal_note'
6
+
7
+ module Cherby
8
+ # Wrapper for Cherwell incident objects.
9
+ class Incident < BusinessObject
10
+ @object_name = 'Incident'
11
+ @template = 'incident'
12
+ @default_values = {
13
+ :service => "Auto Generated",
14
+ :service_group => "Auto Generated",
15
+ :category => "Auto Generated",
16
+ :sub_category => "JIRA",
17
+ :impact => "Inconvenience",
18
+ :urgency => "Medium",
19
+ :priority => "3",
20
+ }
21
+
22
+ def id
23
+ self['IncidentID']
24
+ end
25
+
26
+ # Return true if this incident already exists in Cherwell
27
+ # (that is, its id is a nonempty string of digits)
28
+ def exists?
29
+ return id.to_s =~ /\d+/
30
+ end
31
+
32
+ # Mark this incident as complete by filling in relevant fields.
33
+ # FIXME: Parameterize these, instead of assuming Jira relationship.
34
+ #
35
+ def complete!(comments = "Closed in Jira => automated close.")
36
+ self["CloseDescription"] = comments
37
+ self["LastModDateTime"] = DateTime.now.to_s
38
+ self["Stat_NumberOfTouches"] = self["Stat_NumberOfTouches"].to_i + 1
39
+ #self["PhaseInvestigateStatus"] = "Complete"
40
+ self["PhaseResolveStatus"] = "Complete"
41
+ #self["PhaseCloseStatus"] = "Complete"
42
+ self["ClosureCode"] = "Completed"
43
+ self["CMDBUpdate"] = "No"
44
+ self["BusinessService"] = "Not Applicable"
45
+ self["RequestType"] = "Not Applicable"
46
+ self["SubCategory"] = "JIRA"
47
+ self["SubcategoryNonHR"] = "JIRA"
48
+ end
49
+
50
+ # Return Task instances for all tasks associated with this Incident
51
+ #
52
+ # @return [Array<Task>]
53
+ #
54
+ def tasks
55
+ @tasks ||= @dom.css("BusinessObject[@Name=Task]").map do |element|
56
+ Task.new(element.to_xml)
57
+ end
58
+ end
59
+
60
+ # Return all journal notes associated with this Incident.
61
+ #
62
+ # @return [Array<JournalNote>]
63
+ #
64
+ def journal_notes
65
+ css = "Relationship[@Name='Incident has Notes']" +
66
+ " BusinessObject[@Name=JournalNote]"
67
+ @notes ||= @dom.css(css).map do |element|
68
+ JournalNote.new(element.to_xml)
69
+ end
70
+ end
71
+
72
+ # Add a new Task to this incident.
73
+ def add_task(task_description = "", task_notes = "", owned_by = "")
74
+ # Bail out if this incident doesn't actually exist
75
+ return nil if !exists?
76
+ task = Task.create({
77
+ :parent_public_id => self['IncidentID'],
78
+ :parent_type_name => 'Incident',
79
+ :task_type => 'Action',
80
+ :task_description => task_description,
81
+ :notes => task_notes,
82
+ :owned_by => owned_by,
83
+ })
84
+ relationship_xml = Mustache.render_file('task_relationship',
85
+ {:task_business_object => task.dom.css('BusinessObject').to_xml})
86
+ @dom.css('RelationshipList').first.add_child(relationship_xml)
87
+ end
88
+
89
+ # Add a new JournalNote to this incident.
90
+ #
91
+ # @param [JournalNote] journal_note
92
+ # The note to add to the incident
93
+ #
94
+ def add_journal_note(journal_note)
95
+ return nil if !exists?
96
+ relationship_xml = Mustache.render_file('journal_note_relationship',
97
+ {:note_business_object => journal_note.dom.css('BusinessObject').to_xml})
98
+ @dom.css('RelationshipList').first.add_child(relationship_xml)
99
+ end
100
+
101
+ # Return True if this Incident has important fields differing from the
102
+ # given Incident.
103
+ #
104
+ # @param [Incident] incident
105
+ # The Incident to compare this one to.
106
+ #
107
+ # @return [Boolean]
108
+ # `true` if the incidents differn `false` otherwise.
109
+ #
110
+ def differs_from?(incident)
111
+ return true if self['Status'] != incident['Status']
112
+ return true if self['JIRAID'] != incident['JIRAID']
113
+ return false
114
+ end
115
+
116
+ # DO NOT REMOVE: use the follow code for code-gen of Cherwell consts
117
+ =begin
118
+ def extract_lines(css, l)
119
+ lines = []
120
+ @dom.css(css).each do |f|
121
+ lines << " #{('"' + f["Name"] + '"').ljust(l)} => \"#{f["IDREF"]}\""
122
+ end
123
+ lines
124
+ end
125
+
126
+ def extract
127
+ puts "*"*80
128
+ puts "{"
129
+ puts extract_lines("BusinessObject[@Name=Incident] Field", 40).join(",\n")
130
+ puts "}"
131
+ puts "*"*80
132
+ puts "{"
133
+ extract_lines("BusinessObject[@Name=Task] Field", 24).join(",\n")
134
+ puts "}"
135
+ puts "*"*80
136
+ end
137
+ =end
138
+
139
+ end # class Incident
140
+ end # module Cherby
141
+
@@ -0,0 +1,13 @@
1
+ require 'date'
2
+ require 'cherby/business_object'
3
+
4
+ module Cherby
5
+ class JournalNote < BusinessObject
6
+ @object_name = 'JournalNote'
7
+ @template = 'journal_note'
8
+ @default_values = {
9
+ :details => "",
10
+ }
11
+ end
12
+ end
13
+
@@ -0,0 +1,37 @@
1
+ require 'date'
2
+ require 'cherby/business_object'
3
+
4
+ module Cherby
5
+ class Task < BusinessObject
6
+ @object_name = 'Task'
7
+ @template = 'task'
8
+ @default_values = {
9
+ :status => "New",
10
+ }
11
+
12
+ def id
13
+ self['TaskID']
14
+ end
15
+
16
+ def exists?
17
+ return !id.to_s.empty?
18
+ end
19
+
20
+ # Return True if this Task has important fields differing from the given Task.
21
+ def differs_from?(task)
22
+ return true if self['Status'] != task['Status']
23
+ return false
24
+ end
25
+
26
+ # Add a JournalNote to this Task. Since Tasks cannot directly have JournalNotes
27
+ # associated with them, this just appends the note's content to the Technician Notes
28
+ # field (aka 'CompletionDetails') in the Task.
29
+ def add_journal_note(journal_note)
30
+ message = "\n============================\n" + \
31
+ "Comment added #{journal_note.mod_s} by #{journal_note['CreatedBy']}: " + \
32
+ journal_note['Details'] + "\n\n"
33
+ self['CompletionDetails'] = self['CompletionDetails'] + message
34
+ end
35
+ end
36
+ end # module Cherby
37
+
@@ -0,0 +1,226 @@
1
+ <?xml version="1.0"?>
2
+ <BusinessObject Name="Incident" RecID="{{incident_id}}">
3
+ <FieldList>
4
+ <!-- Fields common to all BusinessObjects -->
5
+ <Field Name="RecID">{{rec_id}}</Field>
6
+ <Field Name="CreatedDateTime">{{created_date_time}}</Field>
7
+ <Field Name="CreatedBy">{{created_by}}</Field>
8
+ <Field Name="CreatedByID"></Field>
9
+ <Field Name="LastModDateTime">{{last_mod_date_time}}</Field>
10
+ <Field Name="LastModBy">{{last_mod_by}}</Field>
11
+ <Field Name="LastModByID"></Field>
12
+ <Field Name="OwnedBy">{{owned_by}}</Field>
13
+ <Field Name="OwnedByTeam">{{owned_by_team}}</Field>
14
+ <Field Name="OwnerID"></Field>
15
+ <Field Name="OwnerTeamID"></Field>
16
+
17
+ <!-- Unused fields? -->
18
+ <Field Name="LastModTimeStamp"/>
19
+
20
+ <Field Name="IncidentID">{{incident_id}}</Field>
21
+ <Field Name="Status">{{status}}</Field>
22
+ <Field Name="StatusDesc"/>
23
+ <Field Name="Service">{{service}}</Field>
24
+ <Field Name="Category">{{category}}</Field>
25
+ <Field Name="SubCategory">{{sub_category}}</Field>
26
+ <Field Name="SpecificsTypeId"/>
27
+ <Field Name="Description">{{description}}</Field>
28
+ <Field Name="Impact">{{impact}}</Field>
29
+ <Field Name="Urgency">{{urgency}}</Field>
30
+ <Field Name="Priority">{{priority}}</Field>
31
+ <Field Name="ClosedDateTime"/>
32
+ <Field Name="ClosedBy"/>
33
+ <Field Name="ClosedByID"/>
34
+ <Field Name="CauseCode"/>
35
+ <Field Name="IsPrivate">FALSE</Field>
36
+ <Field Name="Cost">0</Field>
37
+ <Field Name="CustomerTypeID">{{customer_type_id}}</Field>
38
+ <Field Name="CustomerRecID">{{customer_rec_id}}</Field>
39
+ <Field Name="CloseDescription"/>
40
+ <Field Name="LinkedProblem"/>
41
+ <Field Name="ConfigItemTypeID"/>
42
+ <Field Name="ConfigItemRecID"/>
43
+ <Field Name="IncidentDurationInDays">0.00</Field>
44
+ <Field Name="IncidentType">Service Request</Field>
45
+ <Field Name="RespondBy"/>
46
+ <Field Name="SLAID">93aec497070aea63541fae4004988811547a5a70b9</Field>
47
+ <Field Name="SLAName">Technology Services</Field>
48
+ <Field Name="ResolveBy"/>
49
+ <Field Name="ClosedOn1stCall">FALSE</Field>
50
+ <Field Name="CallSource">e-mail</Field>
51
+ <Field Name="Detail"/>
52
+ <Field Name="ChangeID"/>
53
+ <Field Name="MultipleConfig">FALSE</Field>
54
+ <Field Name="OwnedByEmail"/>
55
+ <Field Name="IncidentDurationInHours">0</Field>
56
+ <Field Name="CustomerDisplayName">{{created_by}}</Field>
57
+ <Field Name="EMailTemplate"/>
58
+ <Field Name="OwnedByManager"/>
59
+ <Field Name="OwnedByManagerEmail"/>
60
+ <Field Name="CreatedByEmail">{{created_by_email}}</Field>
61
+ <Field Name="InitialITContact">{{created_by}}</Field>
62
+ <Field Name="InitialITContactEmail">{{created_by_email}}</Field>
63
+ <Field Name="PendingReason"/>
64
+ <Field Name="ReviewByDate"/>
65
+ <Field Name="ProjectCode"/>
66
+ <Field Name="Task"/>
67
+ <Field Name="PhaseRecordStatus">Complete</Field>
68
+ <Field Name="PhaseClassifyStatus">Complete</Field>
69
+ <Field Name="PhaseInvestigateStatus">In Progress</Field>
70
+ <Field Name="PhaseRFWStatus">Not Required</Field>
71
+ <Field Name="PhaseResolveStatus">Required</Field>
72
+ <Field Name="PhaseCloseStatus">Required</Field>
73
+ <Field Name="PhaseApprovalsStatus">Not Required</Field>
74
+ <Field Name="WrkflwCurrentPhase"/>
75
+ <Field Name="OutsourcedService">FALSE</Field>
76
+ <Field Name="OutsourcedVendorID"/>
77
+ <Field Name="OutsourcedVendorName"/>
78
+ <Field Name="Stat_NumberOfTouches">2</Field>
79
+ <Field Name="Stat_FirstCallResolution">TRUE</Field>
80
+ <Field Name="Stat_IncidentEscalated">FALSE</Field>
81
+ <Field Name="Stat_NumberOfEscalations">0</Field>
82
+ <Field Name="Stat_24x7ElapsedTime">0</Field>
83
+ <Field Name="Stat_IncidentReopened">FALSE</Field>
84
+ <Field Name="Stat_DateTimeResponded">0001-01-01T00:00:00</Field>
85
+ <Field Name="Stat_ResponseTime">0</Field>
86
+ <Field Name="Stat_SLAResponseBreached">FALSE</Field>
87
+ <Field Name="Stat_SLAResolutionBreached">FALSE</Field>
88
+ <Field Name="Stat_TotalDLHOfIncident">0</Field>
89
+ <Field Name="Test_embeddedFormPicker"/>
90
+ <Field Name="LastModByEmail">{{last_mod_by_email}}</Field>
91
+ <Field Name="Temp_reopen">FALSE</Field>
92
+ <Field Name="Temp_pending">FALSE</Field>
93
+ <Field Name="Temp_open">FALSE</Field>
94
+ <Field Name="Temp_status_set">FALSE</Field>
95
+ <Field Name="Temp_Active">FALSE</Field>
96
+ <Field Name="Temp_Accepted">FALSE</Field>
97
+ <Field Name="Temp_Resolved">FALSE</Field>
98
+ <Field Name="Temp_Assigned">FALSE</Field>
99
+ <Field Name="ServiceID">8D71BF81-EBA3-4152-BF34-DDF4931D0A0D</Field>
100
+ <Field Name="PendingPreviousStatus"/>
101
+ <Field Name="SelfServiceUrgent"/>
102
+ <Field Name="SelfServiceMultipleUsers"/>
103
+ <Field Name="SelfServiceAltContactInfo"/>
104
+ <Field Name="SelfServiceContactInfoCorrect">FALSE</Field>
105
+ <Field Name="MatchingText">SHRM Store Conference Setup</Field>
106
+ <Field Name="Notes"/>
107
+ <Field Name="NoteEntry"/>
108
+ <Field Name="AutomateAvailable">FALSE</Field>
109
+ <Field Name="SLAIDForCustomer"/>
110
+ <Field Name="SLAIDForService">93aec497070aea63541fae4004988811547a5a70b9</Field>
111
+ <Field Name="SLAIDForCI"/>
112
+ <Field Name="LinkedSLAs"> , 93aec497070aea63541fae4004988811547a5a70b9 , </Field>
113
+ <Field Name="SLANameForCI"/>
114
+ <Field Name="SLANameForCustomer"/>
115
+ <Field Name="SLANameForService">Technology Services</Field>
116
+ <Field Name="TEMP_showSLAStuff">FALSE</Field>
117
+ <Field Name="VendorName"/>
118
+ <Field Name="VendorID"/>
119
+ <Field Name="ReasonForResolveByBreach"/>
120
+ <Field Name="ConfigItemDisplayName"/>
121
+ <Field Name="CICritical">FALSE</Field>
122
+ <Field Name="ResolveByBreachNotes"/>
123
+ <Field Name="OneStepPicker"/>
124
+ <Field Name="ShowAllServices">FALSE</Field>
125
+ <Field Name="ShowContactInformation">FALSE</Field>
126
+ <Field Name="ServiceEntitlements"/>
127
+ <Field Name="ServiceCustomerIsEntitled">FALSE</Field>
128
+ <Field Name="ServiceGroup">{{service_group}}</Field>
129
+ <Field Name="Environment">PRD</Field>
130
+ <Field Name="Temp_EscalationTeam">Network and Systems Operations</Field>
131
+ <Field Name="PriorityGroup">Less Critical</Field>
132
+ <Field Name="BlockID"/>
133
+ <Field Name="ApprovalRequired">No</Field>
134
+ <Field Name="Approved">FALSE</Field>
135
+ <Field Name="OwnedByNonCherwellUser">FALSE</Field>
136
+ <Field Name="Temp_SurveyCreated">FALSE</Field>
137
+ <Field Name="RespondByWarning">2011-10-24T08:12:00</Field>
138
+ <Field Name="ResolveByWarning">2011-10-27T13:11:52</Field>
139
+ <Field Name="Stat_SLAResolutionBreachedWarning">FALSE</Field>
140
+ <Field Name="Stat_SLAResponseBreachedWarning">FALSE</Field>
141
+ <Field Name="ReasonForRespondByBreach"/>
142
+ <Field Name="RespondByBreachNotes"/>
143
+ <Field Name="HRAuthorized">FALSE</Field>
144
+ <Field Name="CMDBUpdate"/>
145
+ <Field Name="ServiceOutageStart">0001-01-01T00:00:00</Field>
146
+ <Field Name="ServiceOutageEnd">0001-01-01T00:00:00</Field>
147
+ <Field Name="Temp_ServiceGroup">{{service_group}}</Field>
148
+ <Field Name="ClosureCode"/>
149
+ <Field Name="Temp_responded"/>
150
+ <Field Name="TasksExist">FALSE</Field>
151
+ <Field Name="ResolvedDate">0001-01-01T00:00:00</Field>
152
+ <Field Name="CustomerDept">Strategy and Architecture</Field>
153
+ <Field Name="Temp_DefaultTeam">Service Desk</Field>
154
+ <Field Name="SubcategoryNonHR">{{sub_category_non_hr}}</Field>
155
+ <Field Name="ParatureTicketID"/>
156
+ <Field Name="CustomerEmail">{{customer_email}}</Field>
157
+ <Field Name="JIRAID">{{jira_id}}</Field>
158
+ </FieldList>
159
+ <RelationshipList>
160
+ <!--
161
+ <Relationship Name="IncidentLinkedToCustomer" TargetObjectName="Customer" Type="Linked">
162
+ <BusinessObject Name="ADCustomers">Yudkovsky, Sergey</BusinessObject>
163
+ </Relationship>
164
+
165
+ <Relationship Name="IncidentLinkedToCustomer" TargetObjectID="9337c2311b8e8044aa3e2a48c4a95a9f5555815126" TargetObjectName="Customer" Type="Linked">
166
+ <BusinessObject Name="ADCustomers" RecID="93bddda849f81c4ad9ed8d4e158523372c31f24072">Moelk, Brian</BusinessObject>
167
+ </Relationship>
168
+
169
+ <Relationship Name="Incident Links SLA" TargetObjectID="933c73c2163058a1ca063b4a879ae21e1e5133f57c" TargetObjectName="SLA" Type="Linked">
170
+ <BusinessObject Name="SLA" RecID="93aec497070aea63541fae4004988811547a5a70b9">Technology Services</BusinessObject>
171
+ </Relationship>
172
+
173
+ <Relationship Name="Incident Has Tasks" TargetObjectID="9355d5ed41e384ff345b014b6cb1c6e748594aea5b" TargetObjectName="Task" Type="Owns">
174
+ <BusinessObject Name="Task" RecID="93be05329e0fcf411b5e574030a626b90bd297469f"></BusinessObject>
175
+ </Relationship>
176
+
177
+ <Relationship Name="Incident has Service" TargetObjectID="9366b3bb9e94d86b5d5f434ff3b657c4ec5bfa3bb3" TargetObjectName="Service" Type="Linked">
178
+ <BusinessObject Name="Service" RecID="8D71BF81-EBA3-4152-BF34-DDF4931D0A0D">SHRM Store</BusinessObject>
179
+ </Relationship>
180
+
181
+ <Relationship Name="Incident has Service SLA" TargetObjectID="933c73c2163058a1ca063b4a879ae21e1e5133f57c" TargetObjectName="SLA" Type="Linked">
182
+ <BusinessObject Name="SLA" RecID="93aec497070aea63541fae4004988811547a5a70b9">Technology Services</BusinessObject>
183
+ </Relationship>
184
+
185
+ <Relationship Name="Incident Links Service Group" TargetObjectID="93ae4fd3af6355802e542349ebafd6deab1dca3cfb" TargetObjectName="ServiceGroup" Type="Linked">
186
+ <BusinessObject Name="ServiceGroup" RecID="93ae5021c82196035ca3374845956ce9a66eeb08cc">Technology Services</BusinessObject>
187
+ </Relationship>
188
+ -->
189
+
190
+ <!-- Include this only if there's a related object to link to
191
+ <Relationship Name="Incident related to other similar incidents" TargetObjectID="6dd53665c0c24cab86870a21cf6434ae" TargetObjectName="Incident" Type="Linked">
192
+ <BusinessObject Name="Incident" RecID="{{parent_id}}">{{parent_id}}</BusinessObject>
193
+ </Relationship>
194
+ -->
195
+
196
+ <!-- This chunk including WebsiteForm is required when using Service = "Websites (SHRM)".
197
+ Unfortunately, it seems to require that the WebsiteForm object be created first.
198
+ -->
199
+ <!--
200
+ <Relationship Name="IncidentHasSpecifics" TargetObjectID="" TargetObjectName="Specifics" Type="Owns">
201
+ <BusinessObject Name="WebsiteForm" RecID="">
202
+ <FieldList>
203
+ <Field Name="RecID"></Field>
204
+ <Field Name="CreatedDateTime">{{now}}</Field>
205
+ <Field Name="CreatedBy">Yudkovsky, Sergey</Field>
206
+ <Field Name="CreatedByID">93b10b33b94d800e69d2294942a6fc764cbfa82940</Field>
207
+ <Field Name="SpecificTypeID"></Field>
208
+ <Field Name="SpecificTypeName">Website Form</Field>
209
+ <Field Name="ParentId">{{parent_id}}</Field>
210
+ <Field Name="LastModDateTime">{{now}}</Field>
211
+ <Field Name="LastModBy">Yudkovsky, Sergey</Field>
212
+ <Field Name="LastModByID">93b10b33b94d800e69d2294942a6fc764cbfa82940</Field>
213
+ <Field Name="ADCustomersID"/>
214
+ <Field Name="NeedByDate">0001-01-01T00:00:00</Field>
215
+ <Field Name="URLInfo">jira.shrm.org</Field>
216
+ <Field Name="EstimatedTime"/>
217
+ <Field Name="SprintTime"/>
218
+ </FieldList>
219
+ </BusinessObject>
220
+ </Relationship>
221
+ -->
222
+
223
+ </RelationshipList>
224
+ </BusinessObject>
225
+
226
+