private void initChannelMapping(ApiKey apiKey, final List<CalendarConfig> calendarConfigs) {
    bodyTrackHelper.deleteChannelMappings(apiKey);
    ChannelMapping mapping = new ChannelMapping();
    mapping.deviceName = "google_calendar";
    mapping.channelName = "events";
    mapping.timeType = ChannelMapping.TimeType.gmt;
    mapping.channelType = ChannelMapping.ChannelType.timespan;
    mapping.guestId = apiKey.getGuestId();
    mapping.apiKeyId = apiKey.getId();
    bodyTrackHelper.persistChannelMapping(mapping);

    BodyTrackHelper.ChannelStyle channelStyle = new BodyTrackHelper.ChannelStyle();
    channelStyle.timespanStyles = new BodyTrackHelper.MainTimespanStyle();
    channelStyle.timespanStyles.defaultStyle = new BodyTrackHelper.TimespanStyle();
    channelStyle.timespanStyles.defaultStyle.fillColor = "#92da46";
    channelStyle.timespanStyles.defaultStyle.borderColor = "#92da46";
    channelStyle.timespanStyles.defaultStyle.borderWidth = 2;
    channelStyle.timespanStyles.defaultStyle.top = 1.0;
    channelStyle.timespanStyles.defaultStyle.bottom = 1.0;
    channelStyle.timespanStyles.values = new HashMap();

    GoogleCalendarConnectorSettings connectorSettings =
        (GoogleCalendarConnectorSettings) settingsService.getConnectorSettings(apiKey.getId());
    int n = calendarConfigs.size();
    if (connectorSettings != null) {
      n = 0;
      for (CalendarConfig calendar : connectorSettings.calendars) {
        if (!calendar.hidden) n++;
      }
    }
    double rowHeight = 1.f / (n * 2 + 1);
    int i = 0;
    for (CalendarConfig config : calendarConfigs) {
      if (connectorSettings != null && config.hidden) continue;

      BodyTrackHelper.TimespanStyle stylePart = new BodyTrackHelper.TimespanStyle();

      final int rowsFromTop = (i + 1) * 2 - 1;

      stylePart.top = (double) rowsFromTop * rowHeight - (rowHeight * 0.25);
      stylePart.bottom = stylePart.top + rowHeight + (rowHeight * 0.25);
      stylePart.fillColor = config.backgroundColor;
      stylePart.borderColor = config.backgroundColor;
      channelStyle.timespanStyles.values.put(config.id, stylePart);
      i++;
    }

    bodyTrackHelper.setDefaultStyle(apiKey.getGuestId(), "google_calendar", "events", channelStyle);
  }
 @GET
 @Path("/settings/{apiKeyId}")
 @Produces({MediaType.APPLICATION_JSON})
 public String getConnectorSettings(@PathParam("apiKeyId") long apiKeyId)
     throws UpdateFailedException, IOException {
   final ApiKey apiKey = guestService.getApiKey(apiKeyId);
   final long guestId = AuthHelper.getGuestId();
   if (apiKey.getGuestId() != guestId)
     throw new RuntimeException("attempt to retrieve ApiKey from another guest!");
   final Object settings = settingsService.getConnectorSettings(apiKey.getId());
   String json = mapper.writeValueAsString(settings);
   return json;
 }
 @POST
 @Path("/settings/{apiKeyId}")
 @Produces({MediaType.APPLICATION_JSON})
 public StatusModel saveConnectorSettings(
     @PathParam("apiKeyId") long apiKeyId, @FormParam("json") String json) {
   final ApiKey apiKey = guestService.getApiKey(apiKeyId);
   final long guestId = AuthHelper.getGuestId();
   try {
     if (apiKey.getGuestId() != guestId)
       throw new RuntimeException("attempt to retrieve ApiKey from another guest!");
     settingsService.saveConnectorSettings(apiKey.getId(), json);
   } catch (Throwable e) {
     return new StatusModel(false, e.getMessage());
   }
   return new StatusModel(true, "saved connector settings");
 }
  private Calendar getCalendar(final ApiKey apiKey) throws UpdateFailedException {
    HttpTransport httpTransport = new NetHttpTransport();
    JacksonFactory jsonFactory = new JacksonFactory();
    // Get all the attributes for this connector's oauth token from the stored attributes
    final String accessToken = guestService.getApiKeyAttribute(apiKey, "accessToken");
    final String refreshToken = guestService.getApiKeyAttribute(apiKey, "refreshToken");
    final String clientId = guestService.getApiKeyAttribute(apiKey, "google.client.id");
    final String clientSecret = guestService.getApiKeyAttribute(apiKey, "google.client.secret");
    final GoogleCredential.Builder builder = new GoogleCredential.Builder();
    builder.setTransport(httpTransport);
    builder.setJsonFactory(jsonFactory);
    builder.setClientSecrets(clientId, clientSecret);
    GoogleCredential credential = builder.build();
    final Long tokenExpires = Long.valueOf(guestService.getApiKeyAttribute(apiKey, "tokenExpires"));
    credential.setExpirationTimeMilliseconds(tokenExpires);
    credential.setAccessToken(accessToken);
    credential.setRefreshToken(refreshToken);
    try {
      if (tokenExpires < System.currentTimeMillis()) {
        boolean tokenRefreshed = false;

        // Don't worry about checking if we are running on a mirrored test instance.
        // Refreshing tokens independently on both the main server and a mirrored instance
        // seems to work just fine.

        // Try to swap the expired access token for a fresh one.
        tokenRefreshed = credential.refreshToken();

        if (tokenRefreshed) {
          Long newExpireTime = credential.getExpirationTimeMilliseconds();
          logger.info(
              "google calendar token has been refreshed, new expire time = " + newExpireTime);
          // Update stored expire time
          guestService.setApiKeyAttribute(apiKey, "accessToken", credential.getAccessToken());
          guestService.setApiKeyAttribute(apiKey, "tokenExpires", newExpireTime.toString());
        }
      }
    } catch (TokenResponseException e) {
      logger.warn(
          "module=GoogleCalendarUpdater component=background_updates action=refreshToken"
              + " connector="
              + apiKey.getConnector().getName()
              + " guestId="
              + apiKey.getGuestId()
              + " status=permanently failed");
      // Notify the user that the tokens need to be manually renewed
      notificationsService.addNamedNotification(
          apiKey.getGuestId(),
          Notification.Type.WARNING,
          connector().statusNotificationName(),
          "Heads Up. We failed in our attempt to automatically refresh your Google Calendar authentication tokens.<br>"
              + "Please head to <a href=\"javascript:App.manageConnectors()\">Manage Connectors</a>,<br>"
              + "scroll to the Google Calendar connector, and renew your tokens (look for the <i class=\"icon-resize-small icon-large\"></i> icon)");

      // Record permanent update failure since this connector is never
      // going to succeed
      guestService.setApiKeyStatus(
          apiKey.getId(), ApiKey.Status.STATUS_PERMANENT_FAILURE, Utils.stackTrace(e));
      throw new UpdateFailedException(
          "refresh token attempt permanently failed due to a bad token refresh response", e, true);
    } catch (IOException e) {
      logger.warn(
          "module=GoogleCalendarUpdater component=background_updates action=refreshToken"
              + " connector="
              + apiKey.getConnector().getName()
              + " guestId="
              + apiKey.getGuestId()
              + " status=temporarily failed");
      // Notify the user that the tokens need to be manually renewed
      throw new UpdateFailedException("refresh token attempt failed", e, true);
    }
    final Calendar.Builder calendarBuilder =
        new Calendar.Builder(httpTransport, jsonFactory, credential);
    final Calendar calendar = calendarBuilder.build();
    return calendar;
  }