Last active
December 17, 2016 21:31
-
-
Save AlphaGit/11253803388829d7d051 to your computer and use it in GitHub Desktop.
Google Script: automatically logging calendar times to JIRA
This file contains hidden or 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
function logHoursForToday() { | |
setDatesToYesterday(); | |
var options = readOptions(); | |
logHours(options); | |
} | |
function setDatesToYesterday() { | |
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setup"); | |
// based on https://developers.google.com/adwords/scripts/docs/features/dates#creating_a_date_object_from_a_formatted_date_string | |
var today = new Date(); | |
var timeZone = CalendarApp.getTimeZone(); | |
var endDate = Utilities.formatDate(today, timeZone, 'MMMM dd, yyyy 00:00:00 Z') | |
var yesterday = new Date(today - (24 * 60 * 60 * 1000)); | |
var startDate = Utilities.formatDate(yesterday, timeZone, 'MMMM dd, yyyy 00:00:00 Z') | |
sheet.getRange("E1").setValue(startDate); | |
sheet.getRange("E2").setValue(endDate); | |
} | |
function arrayFilter(array, evaluator) { | |
var newArray = []; | |
for (var index = 0; index < array.length; index++) { | |
var value = array[index]; | |
if (evaluator(value)) { | |
newArray.push(value); | |
} | |
} | |
return newArray; | |
} | |
function logHours(options) { | |
if (!options) { | |
options = readOptions(); | |
} | |
if (!validateOptions(options)) return; | |
for (var counter = 0; counter < options.calendarNames.length; counter++) { | |
var calendarName = options.calendarNames[counter]; | |
var jiraTicketID = options.jiraTicketIDs[counter]; | |
var calendar = CalendarApp.getCalendarsByName(calendarName)[0]; | |
if (!calendar) continue; | |
var calendarEvents = calendar.getEvents(options.dateFrom, options.dateTo); | |
for (var calendarIndex = 0; calendarIndex < calendarEvents.length; calendarIndex++) { | |
var calendarEvent = calendarEvents[calendarIndex]; | |
var title = calendarEvent.getTitle(); | |
var eventStartTime = calendarEvent.getStartTime(); | |
var eventEndTime = calendarEvent.getEndTime(); | |
var duration = ((eventEndTime - eventStartTime) / (1000 * 60)).toString() + "m"; | |
createWorklogInJIRA(options.jiraLocation, options.jiraUserName, options.jiraPassword, jiraTicketID, title, eventStartTime, duration); | |
} | |
} | |
} | |
function createWorklogInJIRA(baseUrl, userName, password, ticketId, worklogDescription, worklogStartTime, worklogDuration) { | |
// JIRA REST APIs: https://docs.atlassian.com/jira/REST/latest/ | |
// from: https://developer.atlassian.com/jiradev/api-reference/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-basic-authentication | |
var authorizationHeaderValue = "Basic " + Utilities.base64Encode(userName + ":" + password); | |
var fullTicketUrl = baseUrl + "/rest/tempo-rest/1.0/worklogs/" + ticketId; | |
var timeZone = CalendarApp.getTimeZone(); | |
var ansiWorklogDate = Utilities.formatDate(worklogStartTime, timeZone, 'yyyy-MM-dd'); | |
var tempoWorklogDate = Utilities.formatDate(worklogStartTime, timeZone, 'MMM/dd/yy'); | |
var payload = { | |
planning: false, | |
user: userName, | |
issue: ticketId, | |
ansidate: ansiWorklogDate, | |
ansienddate: ansiWorklogDate, | |
date: tempoWorklogDate, | |
enddate: tempoWorklogDate, | |
time: worklogDuration, | |
remainingEstimate: 0, | |
comment: worklogDescription | |
}; | |
var response = UrlFetchApp.fetch(fullTicketUrl, { | |
method: "post", | |
headers: { | |
Authorization: authorizationHeaderValue | |
}, | |
muteHttpExceptions: true, | |
payload: payload | |
}); | |
var responseCode = response.getResponseCode(); | |
var responseContent = response.getContentText(); | |
if (responseCode >= 300 || responseCode < 200) { | |
Browser.msgBox("Error executing script", "Something went wrong when calling: " + fullTicketUrl + ": " + responseContent, Browser.Buttons.OK); | |
throw "Error while sending data to JIRA"; | |
} | |
var xmlResponse = XmlService.parse(responseContent); | |
if (xmlResponse.getRootElement().getAttribute("valid").getValue() !== "true") { | |
Browser.msgBox("Error executing script", "Invalid Tempo-API submission: " + responseContent, Browser.Buttons.OK); | |
} | |
} | |
function validateOptions(options) { | |
var errors = []; | |
if (!options.dateFrom) errors.push("Date from is required"); | |
if (!options.dateTo) errors.push("Date to is required"); | |
if (options.dateTo < options.dateFrom) errors.push("Date to must be equal or after date from"); | |
if (!options.jiraLocation) errors.push("JIRA Location is required"); | |
if (!options.calendarNames || !options.calendarNames.length) errors.push("At least one calendar name is required"); | |
if (!options.jiraTicketIDs || !options.jiraTicketIDs.length) errors.push("At leats one JIRA ticket is required"); | |
if (options.jiraTicketIDs.length !== options.calendarNames.length) errors.push("There must be a JIRA ticket ID for each calendar"); | |
if (!options.jiraUserName) errors.push("JIRA UserName is required"); | |
if (errors.length) { | |
Browser.msgBox("Validation errors:\n\n" + errors.join("\n- ")); | |
} | |
return !errors.length; | |
} | |
function readOptions() { | |
var optionsSheet = SpreadsheetApp.getActive().getSheetByName("Setup"); | |
var allCalendarNames = optionsSheet.getRange("A2:A100").getValues().map(function (calendarRow) { | |
return calendarRow[0]; | |
}); | |
var calendarNames = arrayFilter(allCalendarNames, function(c) { return !!c; }); | |
var allJiraTicketIDs = optionsSheet.getRange("B2:B100").getValues().map(function (jiraTicketsRow) { | |
return jiraTicketsRow[0]; | |
}); | |
var jiraTicketIDs = arrayFilter(allJiraTicketIDs, function(j) { return !!j; }); | |
return { | |
dateFrom: optionsSheet.getRange("E1").getValue(), | |
dateTo: optionsSheet.getRange("E2").getValue(), | |
jiraLocation: optionsSheet.getRange("E3").getValue(), | |
jiraUserName: optionsSheet.getRange("E4").getValue(), | |
jiraPassword: optionsSheet.getRange("E5").getValue(), | |
calendarNames: calendarNames, | |
jiraTicketIDs: jiraTicketIDs | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment