- save refreshed tokens of both calendar integrations

- Office365 expiry check was off by *1000
- log errors from calendar integrations with console.error
- improved google calendar integration performance further when calendars are selected
This commit is contained in:
Malte Delfs 2021-06-20 17:33:02 +02:00
parent 6d7ec7fa66
commit ded27d17ea

View file

@ -1,14 +1,44 @@
import prisma from "./prisma";
const {google} = require('googleapis'); const {google} = require('googleapis');
import createNewEventEmail from "./emails/new-event"; import createNewEventEmail from "./emails/new-event";
const googleAuth = () => { const googleAuth = (credential) => {
const {client_secret, client_id, redirect_uris} = JSON.parse(process.env.GOOGLE_API_CREDENTIALS).web; const {client_secret, client_id, redirect_uris} = JSON.parse(process.env.GOOGLE_API_CREDENTIALS).web;
return new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); const myGoogleAuth = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
myGoogleAuth.setCredentials(credential.key);
const isExpired = () => myGoogleAuth.isTokenExpiring();
const refreshAccessToken = () => myGoogleAuth.refreshToken(credential.key.refresh_token).then(res => {
const token = res.res.data;
credential.key.access_token = token.access_token;
credential.key.expiry_date = token.expiry_date;
return prisma.credential.update({
where: {
id: credential.id
},
data: {
key: credential.key
}
}).then(() => {
myGoogleAuth.setCredentials(credential.key);
return myGoogleAuth;
});
}).catch(err => {
console.error("Error refreshing google token", err);
return myGoogleAuth;
});
return {
getToken: () => !isExpired() ? Promise.resolve(myGoogleAuth) : refreshAccessToken()
};
}; };
function handleErrorsJson(response) { function handleErrorsJson(response) {
if (!response.ok) { if (!response.ok) {
response.json().then(console.log); response.json().then(e => console.error("O365 Error", e));
throw Error(response.statusText); throw Error(response.statusText);
} }
return response.json(); return response.json();
@ -16,7 +46,7 @@ function handleErrorsJson(response) {
function handleErrorsRaw(response) { function handleErrorsRaw(response) {
if (!response.ok) { if (!response.ok) {
response.text().then(console.log); response.text().then(e => console.error("O365 Error", e));
throw Error(response.statusText); throw Error(response.statusText);
} }
return response.text(); return response.text();
@ -24,9 +54,10 @@ function handleErrorsRaw(response) {
const o365Auth = (credential) => { const o365Auth = (credential) => {
const isExpired = (expiryDate) => expiryDate < +(new Date()); const isExpired = (expiryDate) => expiryDate < Math.round((+(new Date()) / 1000));
const refreshAccessToken = (refreshToken) => fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', { const refreshAccessToken = (refreshToken) => {
return fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({ body: new URLSearchParams({
@ -41,8 +72,16 @@ const o365Auth = (credential) => {
.then((responseBody) => { .then((responseBody) => {
credential.key.access_token = responseBody.access_token; credential.key.access_token = responseBody.access_token;
credential.key.expiry_date = Math.round((+(new Date()) / 1000) + responseBody.expires_in); credential.key.expiry_date = Math.round((+(new Date()) / 1000) + responseBody.expires_in);
return credential.key.access_token; return prisma.credential.update({
where: {
id: credential.id
},
data: {
key: credential.key
}
}).then(() => credential.key.access_token)
}) })
}
return { return {
getToken: () => !isExpired(credential.key.expiry_date) ? Promise.resolve(credential.key.access_token) : refreshAccessToken(credential.key.refresh_token) getToken: () => !isExpired(credential.key.expiry_date) ? Promise.resolve(credential.key.access_token) : refreshAccessToken(credential.key.refresh_token)
@ -173,7 +212,7 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
}) })
} }
).catch((err) => { ).catch((err) => {
console.log(err); console.error(err);
}); });
}, },
createEvent: (event: CalendarEvent) => auth.getToken().then(accessToken => fetch('https://graph.microsoft.com/v1.0/me/calendar/events', { createEvent: (event: CalendarEvent) => auth.getToken().then(accessToken => fetch('https://graph.microsoft.com/v1.0/me/calendar/events', {
@ -206,32 +245,32 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
}; };
const GoogleCalendar = (credential): CalendarApiAdapter => { const GoogleCalendar = (credential): CalendarApiAdapter => {
const myGoogleAuth = googleAuth(); const auth = googleAuth(credential);
myGoogleAuth.setCredentials(credential.key);
const integrationType = "google_calendar"; const integrationType = "google_calendar";
return { return {
getAvailability: (dateFrom, dateTo, selectedCalendars) => new Promise((resolve, reject) => { getAvailability: (dateFrom, dateTo, selectedCalendars) => new Promise((resolve, reject) => auth.getToken().then(myGoogleAuth => {
const calendar = google.calendar({version: 'v3', auth: myGoogleAuth}); const calendar = google.calendar({version: 'v3', auth: myGoogleAuth});
calendar.calendarList const selectedCalendarIds = selectedCalendars.filter(e => e.integration === integrationType).map(e => e.externalId);
.list() if (selectedCalendarIds.length == 0 && selectedCalendars.length > 0){
.then(cals => {
const filteredItems = cals.data.items.filter(i => selectedCalendars.findIndex(e => e.externalId === i.id) > -1)
if (filteredItems.length == 0 && selectedCalendars.length > 0){
// Only calendars of other integrations selected // Only calendars of other integrations selected
resolve([]); resolve([]);
return;
} }
(selectedCalendarIds.length == 0
? calendar.calendarList.list().then(cals => cals.data.items.map(cal => cal.id))
: Promise.resolve(selectedCalendarIds)).then(calsIds => {
calendar.freebusy.query({ calendar.freebusy.query({
requestBody: { requestBody: {
timeMin: dateFrom, timeMin: dateFrom,
timeMax: dateTo, timeMax: dateTo,
items: filteredItems.length > 0 ? filteredItems : cals.data.items items: calsIds.map(id => ({id: id}))
} }
}, (err, apires) => { }, (err, apires) => {
if (err) { if (err) {
reject(err); reject(err);
} }
resolve( resolve(
Object.values(apires.data.calendars).flatMap( Object.values(apires.data.calendars).flatMap(
(item) => item["busy"] (item) => item["busy"]
@ -240,11 +279,12 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
}); });
}) })
.catch((err) => { .catch((err) => {
console.error('There was an error contacting google calendar service: ', err);
reject(err); reject(err);
}); });
}), })),
createEvent: (event: CalendarEvent) => new Promise((resolve, reject) => { createEvent: (event: CalendarEvent) => new Promise((resolve, reject) => auth.getToken().then(myGoogleAuth => {
const payload = { const payload = {
summary: event.title, summary: event.title,
description: event.description, description: event.description,
@ -276,13 +316,13 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
resource: payload, resource: payload,
}, function (err, event) { }, function (err, event) {
if (err) { if (err) {
console.log('There was an error contacting the Calendar service: ' + err); console.error('There was an error contacting google calendar service: ', err);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
}); });
}), })),
updateEvent: (uid: String, event: CalendarEvent) => new Promise((resolve, reject) => { updateEvent: (uid: String, event: CalendarEvent) => new Promise((resolve, reject) => auth.getToken().then(myGoogleAuth => {
const payload = { const payload = {
summary: event.title, summary: event.title,
description: event.description, description: event.description,
@ -317,13 +357,13 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
resource: payload resource: payload
}, function (err, event) { }, function (err, event) {
if (err) { if (err) {
console.log('There was an error contacting the Calendar service: ' + err); console.error('There was an error contacting google calendar service: ', err);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
}); });
}), })),
deleteEvent: (uid: String) => new Promise( (resolve, reject) => { deleteEvent: (uid: String) => new Promise( (resolve, reject) => auth.getToken().then(myGoogleAuth => {
const calendar = google.calendar({version: 'v3', auth: myGoogleAuth}); const calendar = google.calendar({version: 'v3', auth: myGoogleAuth});
calendar.events.delete({ calendar.events.delete({
auth: myGoogleAuth, auth: myGoogleAuth,
@ -333,13 +373,13 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
sendUpdates: 'all', sendUpdates: 'all',
}, function (err, event) { }, function (err, event) {
if (err) { if (err) {
console.log('There was an error contacting the Calendar service: ' + err); console.error('There was an error contacting google calendar service: ', err);
return reject(err); return reject(err);
} }
return resolve(event.data); return resolve(event.data);
}); });
}), })),
listCalendars: () => new Promise((resolve, reject) => { listCalendars: () => new Promise((resolve, reject) => auth.getToken().then(myGoogleAuth => {
const calendar = google.calendar({version: 'v3', auth: myGoogleAuth}); const calendar = google.calendar({version: 'v3', auth: myGoogleAuth});
calendar.calendarList calendar.calendarList
.list() .list()
@ -352,9 +392,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
})) }))
}) })
.catch((err) => { .catch((err) => {
console.error('There was an error contacting google calendar service: ', err);
reject(err); reject(err);
}); });
}) }))
}; };
}; };