Skip to content

Instantly share code, notes, and snippets.

@YoungElPaso
Created November 27, 2017 16:45
Show Gist options
  • Save YoungElPaso/07b698c8eeee6b0b7b1a005e47c715c9 to your computer and use it in GitHub Desktop.
Save YoungElPaso/07b698c8eeee6b0b7b1a005e47c715c9 to your computer and use it in GitHub Desktop.
"use strict";
// node-ews for Exchange Web Services API calls.
const EWS = require("node-ews");
// map-json for mapping JSON to something simpler.
const MP = require("map-json");
// require lodash.
const _ = require("lodash");
// use dedent-js for template indentation issues.
const DD = require("dedent-js");
// JS datetime stuff.
const moment = require("moment");
// Hack, needed to bump sequence, probably a better method!
const replace = require("replace-in-file");
let sequence = 0;
const repOptions = {
files: "beancounter.txt",
from: /sequence:*.*/,
to: match => {
sequence = eval(match.split(":")[1]) + 1;
match = "sequence:" + sequence;
return match;
}
};
replace(repOptions).then(changes => {
doEverything();
});
function doEverything() {
// EWS config object.
const ewsConfig = {
username: "some.one@office365customer.ca",
password: "xxxxxx",
host: "https://outlook.office365.com",
auth: "basic"
};
// Instantiate new EWS.
const ews = new EWS(ewsConfig);
// Basic funtion to get stuff.
const ewsFunction = "FindItem";
// Args for finding stuff.
const ewsArgs = {
attributes: {
Traversal: "Shallow"
},
ItemShape: {
BaseShape: "Default"
},
CalendarView: {
attributes: {
MaxEntriesReturned: "1000",
// Broad range, who cares, if I need this in two years, revisit then.
StartDate: " 2017-10-10T12:00:00Z",
EndDate: " 2019-01-01T12:00:00Z"
}
},
ParentFolderIds: {
DistinguishedFolderId: {
attributes: {
Id: "calendar"
}
}
}
};
// Go get the stuff.
ews
.run(ewsFunction, ewsArgs)
.then(result => {
let res =
result.ResponseMessages.FindItemResponseMessage.RootFolder.Items;
// Does some recursive mapping of old keys to new.
let mappingObj = {
CalendarItems: {
_source: "*",
_transform: {
"@toObjectArray": [
{
SUMMARY: {
_source: "CalendarItem.*.Subject",
_transformEach: { toUpperCase: [] }
},
UID: {
_source: "CalendarItem.*.Start",
_transformEach: { createUID: [] }
},
DTSTART: {
_source: "CalendarItem.*.Start",
_transformEach: { dtIt: [] }
},
DTEND: {
_source: "CalendarItem.*.End",
_transformEach: { dtIt: [] }
},
// TENTATIVE|CONFIRMED|CANCELED
STATUS: {
_source: "CalendarItem.*.LegacyFreeBusyStatus",
_transformEach: {
reMapStatus: [],
toUpperCase: []
}
}
}
]
}
}
};
let transFunctions =
// Transform / condition functions
{
createUID: string => {
let s =
_.replace(string, /-|:/g, "") + "someone@123fakestreet";
return s;
},
reMapStatus: string => {
// TODO make sure the map is correct!
// Meh, probably wrong api so leaving this in. Status not really important, this is read-only
if (string == "Busy" || string == "Free") {
string = "CONFIRMED";
} else {
string = "TENTATIVE";
}
let s = string;
return s;
},
dtStmp: string => {
// Annoying datetime format required for ical.
let d = _replace(new Date.toISOString(), /-|:/g, "");
return d;
},
dtIt: string => {
let s = _.replace(string, /-|:/g, "");
return s;
},
toUpperCase: value => value.toUpperCase(),
substr: (string, from, length) => string.substr(from, length),
toObjectArray: inputObject => {
// Uses lodash
const createObjectByIndex = index =>
_.mapValues(inputObject, array => array[index]);
const firstObjectValues = _.values(inputObject)[0] || [];
return _.times(firstObjectValues.length, createObjectByIndex);
}
};
// Map the old JSON to a more clear structure. Could be skipped.
let mapped = MP.map(res, mappingObj, transFunctions);
// Blank template.
let eventsTpl = "";
// Create an .ics file using a template.
_.each(mapped.CalendarItems, function(e, i) {
eventsTpl += `
BEGIN:VEVENT
SUMMARY:${e.SUMMARY}
UID:${e.UID}
DTSTAMP:${// Brutal hack required for datetime (split on seconds)
_.replace(
moment()
.seconds(0)
.milliseconds(0)
.toISOString()
.split(":")[0] + "1500Z",
/-|:|\./g,
""
)}
DTSTART:${e.DTSTART}
DTEND:${e.DTEND}
STATUS:${e.STATUS}
END:VEVENT`;
});
// Put the template together w/ the wrapping ics strings.
// Sequence is CRUCIAL to getting updates (if it doesn't bump, Google wont reimport!!!)
let icalTpl = `
BEGIN:VCALENDAR
VERSION: 2.0
SEQUENCE:${sequence}
PRODID:-//someone//ews-api-ical//EN
${DD(eventsTpl)}
END:VCALENDAR
`;
console.log(DD(icalTpl));
})
.catch(err => {
console.log(err);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment