diff --git a/src/OutlookGoogleCalendarSync/Console/Console.cs b/src/OutlookGoogleCalendarSync/Console/Console.cs
index 10409d54..e9be9725 100644
--- a/src/OutlookGoogleCalendarSync/Console/Console.cs
+++ b/src/OutlookGoogleCalendarSync/Console/Console.cs
@@ -15,7 +15,18 @@ public class Console {
private String content = "";
public String DocumentText {
get {
- return (this.wb == null ? null : this.wb.DocumentText);
+ String documentText = "";
+ if (this.wb == null)
+ return null;
+ else {
+ if (this.wb.InvokeRequired) {
+ this.wb.Invoke((MethodInvoker)(() => {
+ documentText = wb.DocumentText;
+ }));
+ } else
+ documentText = this.wb.DocumentText;
+ }
+ return documentText;
}
}
@@ -271,7 +282,7 @@ public void Update(String moreOutput, Markup? markupPrefix = null, bool newLine
}
//Don't add append line break to Markup that's already wrapped in
tags
- if (markupPrefix != null && (new Markup[] { Markup.info, Markup.warning, Markup.error }.ToList()).Contains((Markup)markupPrefix))
+ if (markupPrefix != null && (new Markup[] { Markup.info, Markup.warning, Markup.fail, Markup.error }.ToList()).Contains((Markup)markupPrefix))
newLine = false;
contentInnerHtml += htmlOutput + (newLine ? "
" : "");
diff --git a/src/OutlookGoogleCalendarSync/Extensions/Exception.cs b/src/OutlookGoogleCalendarSync/Extensions/Exception.cs
index 3c0b3299..38a0f41f 100644
--- a/src/OutlookGoogleCalendarSync/Extensions/Exception.cs
+++ b/src/OutlookGoogleCalendarSync/Extensions/Exception.cs
@@ -129,7 +129,17 @@ public static System.Exception LogAsFail(System.Exception ex) {
LogAsFail(ref ex);
return ex;
}
-
+
+ ///
+ /// Capture this exception as log4net FAIL (not ERROR) when logged
+ ///
+ public static void LogAsFail(ref Google.GoogleApiException ex) {
+ if (ex.Data.Contains(LogAs))
+ ex.Data[LogAs] = OGCSexception.LogLevel.FAIL;
+ else
+ ex.Data.Add(LogAs, OGCSexception.LogLevel.FAIL);
+ }
+
///
/// Capture this exception as log4net FAIL (not ERROR) when logged
///
diff --git a/src/OutlookGoogleCalendarSync/GoogleOgcs/Authenticator.cs b/src/OutlookGoogleCalendarSync/GoogleOgcs/Authenticator.cs
index 31c13b77..2985dfc7 100644
--- a/src/OutlookGoogleCalendarSync/GoogleOgcs/Authenticator.cs
+++ b/src/OutlookGoogleCalendarSync/GoogleOgcs/Authenticator.cs
@@ -90,7 +90,7 @@ private static ClientSecrets getCalendarClientSecrets() {
return provider;
}
- private async Task getAuthenticated(ClientSecrets cs) {
+ private async Task getAuthenticated(ClientSecrets cs) {
log.Debug("Authenticating with Google calendar service...");
FileDataStore tokenStore = new FileDataStore(Program.UserFilePath);
@@ -141,17 +141,43 @@ private async Task getAuthenticated(ClientSecrets cs) {
log.Debug("Access token needs refreshing.");
//This will happen automatically when using the calendar service
//But we need a valid token before we call getGaccountEmail() which doesn't use the service
- try {
- GoogleOgcs.Calendar.Instance.Service.Settings.Get("useKeyboardShortcuts").Execute();
- } catch (System.Exception ex) {
- if (ex is Google.Apis.Auth.OAuth2.Responses.TokenResponseException)
- OGCSexception.AnalyseTokenResponse(ex as Google.Apis.Auth.OAuth2.Responses.TokenResponseException, false);
- else {
- OGCSexception.Analyse(ex);
- Forms.Main.Instance.Console.Update("Unable to communicate with Google services. " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message), Console.Markup.warning);
+ int backoff = 0;
+ while (backoff < Calendar.backoffLimit) {
+ try {
+ GoogleOgcs.Calendar.Instance.Service.Settings.Get("useKeyboardShortcuts").Execute();
+ } catch (Google.GoogleApiException ex) {
+ switch (Calendar.HandleAPIlimits(ex, null)) {
+ case Calendar.ApiException.throwException: throw;
+ case Calendar.ApiException.freeAPIexhausted:
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(Calendar.Instance.SubscriptionInvite);
+ OGCSexception.LogAsFail(ref aex);
+ authenticated = false;
+ return authenticated;
+ case Calendar.ApiException.backoffThenRetry:
+ backoff++;
+ if (backoff == Calendar.backoffLimit) {
+ log.Fail("API limit backoff was not successful. Retrieving useKeyboardShortcuts setting failed.");
+ authenticated = false;
+ return authenticated;
+ } else {
+ log.Warn("API rate limit reached. Backing off " + backoff + "sec before retry.");
+ System.Threading.Thread.Sleep(backoff * 1000);
+ }
+ break;
+ }
+
+ } catch (System.Exception ex) {
+ if (ex is Google.Apis.Auth.OAuth2.Responses.TokenResponseException)
+ OGCSexception.AnalyseTokenResponse(ex as Google.Apis.Auth.OAuth2.Responses.TokenResponseException, false);
+ else {
+ OGCSexception.Analyse(ex);
+ Forms.Main.Instance.Console.Update("Unable to communicate with Google services. " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message), Console.Markup.warning);
+ }
+ authenticated = false;
+ return authenticated;
}
- authenticated = false;
- return;
}
log.Debug("Access token refreshed.");
}
@@ -159,6 +185,7 @@ private async Task getAuthenticated(ClientSecrets cs) {
getGaccountEmail(credential.Token.AccessToken);
authenticated = true;
Forms.Main.Instance.Console.Update("Handshake successful.", verbose: true);
+ return authenticated;
}
public void Reset(Boolean reauthorise = true) {
@@ -312,7 +339,9 @@ public Boolean UserSubscriptionCheck() {
switch (GoogleOgcs.Calendar.HandleAPIlimits(ex, null)) {
case Calendar.ApiException.throwException: throw;
case Calendar.ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(GoogleOgcs.Calendar.Instance.SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(GoogleOgcs.Calendar.Instance.SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
GoogleOgcs.Calendar.Instance.Service = null;
throw aex;
diff --git a/src/OutlookGoogleCalendarSync/GoogleOgcs/GoogleCalendar.cs b/src/OutlookGoogleCalendarSync/GoogleOgcs/GoogleCalendar.cs
index c4794624..6205f416 100644
--- a/src/OutlookGoogleCalendarSync/GoogleOgcs/GoogleCalendar.cs
+++ b/src/OutlookGoogleCalendarSync/GoogleOgcs/GoogleCalendar.cs
@@ -69,7 +69,7 @@ public CalendarService Service {
set { service = value; }
}
public static Boolean APIlimitReached_attendee = false;
- private const int backoffLimit = 5;
+ public const int backoffLimit = 5;
public enum ApiException {
justContinue,
backoffThenRetry,
@@ -105,7 +105,9 @@ public List GetCalendars() {
switch (HandleAPIlimits(ex, null)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -156,7 +158,9 @@ public List GetCalendarEntriesInRecurrence(String recurringEventId) {
switch (HandleAPIlimits(ex, null)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -207,7 +211,9 @@ public Event GetCalendarEntry(String eventId) {
switch (HandleAPIlimits(ex, null)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -229,8 +235,8 @@ public Event GetCalendarEntry(String eventId) {
else
throw new System.Exception("Returned null");
} catch (System.Exception ex) {
+ if (ex is ApplicationException) throw;
Forms.Main.Instance.Console.Update("Failed to retrieve Google event", Console.Markup.error);
- log.Error(ex.Message);
return null;
}
}
@@ -265,7 +271,9 @@ public List GetCalendarEntriesInRange(DateTime from, DateTime to) {
switch (HandleAPIlimits(ex, null)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -445,7 +453,9 @@ private Event createCalendarEntry_save(Event ev, AppointmentItem ai) {
switch (HandleAPIlimits(ex, ev)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.justContinue: break;
@@ -814,7 +824,9 @@ public void UpdateCalendarEntry_save(ref Event ev) {
switch (HandleAPIlimits(ex, ev)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -906,7 +918,9 @@ private void deleteCalendarEntry_save(Event ev) {
switch (HandleAPIlimits(ex, ev)) {
case ApiException.throwException: throw;
case ApiException.freeAPIexhausted:
- System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite, ex);
+ OGCSexception.LogAsFail(ref ex);
+ OGCSexception.Analyse(ex);
+ System.ApplicationException aex = new System.ApplicationException(SubscriptionInvite);
OGCSexception.LogAsFail(ref aex);
throw aex;
case ApiException.backoffThenRetry:
@@ -1663,7 +1677,7 @@ public static ApiException HandleAPIlimits(Google.GoogleApiException ex, Event e
return ApiException.justContinue;
} else if (ex.Message.Contains("Daily Limit Exceeded") ||
- (ex.Error.Errors.First().Reason == "rateLimitExceeded" && ex.Message.Contains("limit 'Queries per day'"))) {
+ (ex.Message.Contains("limit 'Queries per day'") && ex.Error != null && ex.Error.Errors != null && ex.Error.Errors.First().Reason == "rateLimitExceeded")) {
log.Warn("Google's free Calendar quota has been exhausted! New quota comes into effect 08:00 GMT.");
Forms.Main.Instance.SyncNote(Forms.Main.SyncNotes.QuotaExhaustedInfo, null);
@@ -1696,5 +1710,17 @@ public static ApiException HandleAPIlimits(Google.GoogleApiException ex, Event e
}
}
#endregion
+
+ ///
+ /// This is solely for purposefully causing an error to assist when developing
+ ///
+ private void throwApiException() {
+ Google.GoogleApiException ex = new Google.GoogleApiException("Service", "limit 'Queries per day'");
+ Google.Apis.Requests.SingleError err = new Google.Apis.Requests.SingleError();
+ err.Reason = "rateLimitExceeded";
+ ex.Error = new Google.Apis.Requests.RequestError { Errors = new List() };
+ ex.Error.Errors.Add(err);
+ throw ex;
+ }
}
}
diff --git a/src/OutlookGoogleCalendarSync/Sync/Engine.cs b/src/OutlookGoogleCalendarSync/Sync/Engine.cs
index 221211d6..481770b9 100644
--- a/src/OutlookGoogleCalendarSync/Sync/Engine.cs
+++ b/src/OutlookGoogleCalendarSync/Sync/Engine.cs
@@ -288,7 +288,8 @@ public void Start(Boolean updateSyncSchedule = true) {
}
} else {
consecutiveSyncFails += failedAttempts;
- mainFrm.Console.Update("Sync aborted after " + failedAttempts + " failed attempts!", syncResult == SyncResult.UserCancelled ? Console.Markup.fail : Console.Markup.error);
+ mainFrm.Console.Update("Sync aborted after " + failedAttempts + " failed attempts!",
+ new SyncResult[] { SyncResult.UserCancelled, SyncResult.Abandon }.Contains(syncResult) ? Console.Markup.fail : Console.Markup.error);
}
setNextSync(syncStarted, syncResult == SyncResult.OK, updateSyncSchedule, cacheNextSync);
@@ -499,8 +500,8 @@ private SyncResult synchronize() {
}
}
}
- } catch (System.Exception) {
- console.Update("Failed to retrieve master for Google recurring event outside of sync range.", Console.Markup.error);
+ } catch (System.Exception ex) {
+ console.Update("Failed to retrieve master for Google recurring event outside of sync range.", OGCSexception.LoggingAsFail(ex) ? Console.Markup.fail : Console.Markup.error);
throw;
} finally {
oPattern = (RecurrencePattern)OutlookOgcs.Calendar.ReleaseObject(oPattern);