-
-
Save mrxinu/94e2411a3c12eff3297b34a5902b1784 to your computer and use it in GitHub Desktop.
Import Incidents from WHD export
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
################################################################# | |
## | |
## First install the wrapper & xml parser `gem install samanage nokogiri` | |
## To run `ruby import_whd_tickets.rb api-token export-file.csv` | |
## | |
## | |
## The CSV input columns were: | |
## | |
## 'No.' | |
## 'Status' | |
## 'Priority' | |
## 'Tech' | |
## 'tech email' | |
## 'site' | |
## 'Department' | |
## 'Category' | |
## 'Sub Category' | |
## 'Title' | |
## 'Description' | |
## 'Client' | |
## 'client email' | |
## 'Notes' | |
require "nokogiri" | |
require "samanage" | |
require "csv" | |
api_token, csv_file = ARGV | |
@output_file = "Errors-#{csv_file.split('.').first}-#{DateTime.now.strftime("%b-%d-%Y %H%M")}.csv" | |
@samanage = Samanage::Api.new(token: api_token) | |
unless csv_file | |
puts "Please enter a CSV file" | |
exit | |
end | |
unless @samanage.authorize | |
puts "Invalid API Token: #{api_token}" | |
exit | |
end | |
def log_error(hsh:, filename: @output_file) | |
write_headers = !File.exist?(filename) | |
CSV.open(filename, "a+", write_headers: write_headers, headers: hsh.keys) do |csv| | |
csv << hsh.values | |
end | |
nil | |
end | |
puts "Reading #{csv_file}" | |
whd_tickets = CSV.read(csv_file, headers: true, encoding: "utf-8") | |
.map(&:to_h) | |
def import_ticket(csv_data:) | |
# Tickets can only be created as Closed or New | |
initial_state = nil | |
if !["Closed", "New"].include?(csv_data["Status"]) | |
initial_state = "New" | |
end | |
title = "WHD-#{csv_data['No.']} #{csv_data['Title']}" | |
payload = { incident: {} } | |
payload[:incident] | |
payload[:incident][:requester] = { email: csv_data["client email"] } # required in email format (this column may have been added manually) | |
payload[:incident][:name] = title # required 256 char limit | |
payload[:incident][:description] = csv_data["Description"] | |
payload[:incident][:state] = initial_state || csv_data["Status"] | |
payload[:incident][:custom_fields_values] = {} | |
payload[:incident][:custom_fields_values][:custom_fields_value] = [ | |
# { name: "Some Field", value: csv_data["Custom CSV field"] }, | |
# { name: "Some Other Field", value: csv_data["Custom Data etc"] } | |
] | |
## I create the payload this convoluted way because it is problematic to send nested nil values for the fields below eg: | |
## =====> {incident: {category: {name: nil}, subcategory: {name: nil}}} | |
## You can check the existence of these properties in these resources | |
## api.samanage.com/users.json | |
## api.samanage.com/categories.json (contains subcategories also) | |
## api.samanage.com/sites.json | |
## api.samanage.com/departments.json | |
## Some customers prefer to see a list of nonimported errors | |
## others are okay with inserting blank values if they don't match... | |
payload[:incident][:category] = { name: csv_data["Category"] } if csv_data["Category"] | |
payload[:incident][:subcategory] = { name: csv_data["Sub Category"] } if csv_data["Sub Category"] | |
payload[:incident][:site] = { name: csv_data["Site"] } if csv_data["Site"] | |
payload[:incident][:department] = { name: csv_data["Department"] } if csv_data["Department"] | |
payload[:incident][:assignee] = { email: csv_data["tech email"] } if csv_data["tech email"] # this column may have been added manually | |
puts "\nImporting: #{title}" | |
# This is just a post request to /incidents with error handling | |
incident = @samanage.create_incident(payload: payload) | |
add_comments(incident_id: incident.dig(:data, "id"), csv_data: csv_data) | |
# If there was an initial_state then finish with updating to the correct state | |
if initial_state | |
puts "Setting final incident state to #{csv_data['Status']}" | |
@samanage.update_incident( | |
id: incident.dig(:data, "id"), | |
payload: { incident: { state: csv_data["Status"] } } | |
) | |
end | |
rescue Samanage::Error, Samanage::InvalidRequest => e | |
log_error(hsh: csv_data.merge(error_reason: "Creating Incident", error: "#{e.status_code}: #{e.response}")) | |
end | |
def add_comments(incident_id:, csv_data:) | |
notes = csv_data["Notes"] | |
return unless notes | |
doc = Nokogiri::XML(notes) | |
## Not sure if there can be multiple notes in the regular export the examples I've seen only have 1 | |
parsed_note = doc.at("note") | |
## Commenter must be the email of a user. If commenter is nil then the UI will show the API user as the commenter | |
commenter = parsed_note.attribute("author") ? "#{parsed_note.attribute('author')}@domain.com" : nil | |
payload = { | |
comment: { | |
body: parsed_note.content || parsed_note.text, | |
commenter: commenter, | |
is_private: parsed_note.attribute("type").to_s == "hidden" | |
} | |
} | |
puts "Adding comments" | |
@samanage.create_comment(incident_id: incident_id, comment: payload)\ | |
rescue Samanage::Error, Samanage::InvalidRequest => e | |
log_error(hsh: csv_data.merge(error_reason: "Adding comment", error: "#{e.status_code}: #{e.response}")) | |
rescue => e | |
log_error(hsh: csv_data.merge(error_reason: "Adding comment", error: "Unexpected Error #{e.class}: #{e.inspect}")) | |
end | |
whd_tickets.each do |whd_ticket| | |
import_ticket(csv_data: whd_ticket) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment