public BodymediaUpdater() { super(); ObjectType burn = ObjectType.getObjectType(connector(), "burn"); ObjectType sleep = ObjectType.getObjectType(connector(), "sleep"); ObjectType steps = ObjectType.getObjectType(connector(), "steps"); url.put(burn, "burn/day/minute/intensity/"); url.put(sleep, "sleep/day/period/"); url.put(steps, "step/day/hour/"); maxIncrement.put(burn, 1); maxIncrement.put(sleep, 1); maxIncrement.put(steps, 31); }
public DateTime getStartDate(UpdateInfo updateInfo, ObjectType ot) throws Exception { ApiKey apiKey = updateInfo.apiKey; // The updateStartDate for a given object type is stored in the apiKeyAttributes // as BodyMedia.<objectName>.updateStartDate. In the case of a failure the updater will store // the date // that failed and start there next time. In the case of a successfully completed update it // will store // the lastSync date returned from BodyMedia along with each API call. String updateKeyName = "BodyMedia." + ot.getName() + ".updateStartDate"; String updateStartDate = guestService.getApiKeyAttribute(apiKey, updateKeyName); // The first time we do this there won't be an apiKeyAttribute yet. In that case get the // registration date for the user and store that. if (updateStartDate == null) { OAuthConsumer consumer = setupConsumer(updateInfo.apiKey); String api_key = guestService.getApiKeyAttribute(updateInfo.apiKey, "bodymediaConsumerKey"); updateStartDate = getUserRegistrationDate(updateInfo, api_key, consumer); // Store in the apiKeyAttribute for next time guestService.setApiKeyAttribute(updateInfo.apiKey, updateKeyName, updateStartDate); } try { return formatter.parseDateTime(updateStartDate); } catch (IllegalArgumentException e) { return TimeUtils.dateFormatter.parseDateTime(updateStartDate); } }
private long getLatestData(ApiKey apiKey) { if (!apiKey.getConnector().hasFacets()) return Long.MAX_VALUE; final ObjectType[] objectTypes = apiKey.getConnector().objectTypes(); if (objectTypes == null || objectTypes.length == 0) { final String maxTimeAtt = guestService.getApiKeyAttribute(apiKey, ApiKeyAttribute.MAX_TIME_KEY); // only return the ApiKey's maxTime if we have it cached as an attribute if (maxTimeAtt != null && StringUtils.isNotEmpty(maxTimeAtt)) { final DateTime dateTime = ISODateTimeFormat.dateHourMinuteSecondFraction() .withZoneUTC() .parseDateTime(maxTimeAtt); return dateTime.getMillis(); } } else { long maxTime = Long.MIN_VALUE; for (ObjectType objectType : objectTypes) { final String maxTimeAtt = guestService.getApiKeyAttribute( apiKey, objectType.getApiKeyAttributeName(ApiKeyAttribute.MAX_TIME_KEY)); if (maxTimeAtt != null && StringUtils.isNotEmpty(maxTimeAtt)) { final DateTime dateTime = ISODateTimeFormat.dateHourMinuteSecondFraction() .withZoneUTC() .parseDateTime(maxTimeAtt); final long maxObjectTypeTime = dateTime.getMillis(); if (maxObjectTypeTime > maxTime) maxTime = maxObjectTypeTime; } } // only return the ApiKey's maxTime if we have it cached as an attribute for any its // connector's objectTypes if (maxTime > Long.MIN_VALUE) return maxTime; } // fall back to old method of querying the facets table AbstractFacet facet = null; // apiDataService.getLatestApiDataFacet(apiKey, null); return facet == null ? Long.MAX_VALUE : facet.end; }
// Update the start date for next time. The parameter the updateProgressDate is the date // of that retrieveHistory had gotten to when it completed or gave up. // If lastSync is set and is < updateProgressDate we will use that, and otherwise use // updateProgressDate. void updateStartDate(UpdateInfo updateInfo, ObjectType ot, DateTime updateProgressTime) { DateTimeComparator comparator = DateTimeComparator.getDateOnlyInstance(); // Calculate the name of the key in the ApiAttributes table // where the next start of update for this object type is // stored and retrieve the stored value. This stored value // may potentially be null if something happened to the attributes table String updateKeyName = "BodyMedia." + ot.getName() + ".updateStartDate"; String storedUpdateStartDate = guestService.getApiKeyAttribute(updateInfo.apiKey, updateKeyName); // Retrieve the lastSync date if it has been added to the // updateInfo context by an extractor DateTime lastSync = (DateTime) updateInfo.getContext("lastSync"); // Check which is earlier: the lastSync time returned from Bodymedia or the // point we were in the update that just ended. Store the lesser of the two // in nextUpdateStartDate String nextUpdateStartDate = storedUpdateStartDate; if (lastSync != null) { if (comparator.compare(updateProgressTime, lastSync) > 0) { // lastSync from Bodymedia is less than the update progress nextUpdateStartDate = lastSync.toString(formatter); } else { // the update progress is less than lastSync from Bodymedia nextUpdateStartDate = updateProgressTime.toString(formatter); } } else { // Last sync is null, just leave the stored updateTime // alone since it's better to get some extra data next // time than to skip data from dates that potentially changed } // Store the new value if it's different than what's stored in ApiKeyAttributes if (storedUpdateStartDate == null || !nextUpdateStartDate.equals(storedUpdateStartDate)) { guestService.setApiKeyAttribute(updateInfo.apiKey, updateKeyName, nextUpdateStartDate); } }
/** * Retrieves that history for the given facet from the start date to the end date. It peforms the * api calls in reverse order starting from the end date. This is so that the most recent * information is retrieved first. * * @param updateInfo The api's info * @param ot The ObjectType that represents the facet to be updated * @param start The earliest date for which the burn history is retrieved. This date is included * in the update. * @param end The latest date for which the burn history is retrieved. This date is also included * in the update. * @throws Exception If either storing the data fails or if the rate limit is reached on * Bodymedia's api */ private void retrieveHistory(UpdateInfo updateInfo, ObjectType ot, DateTime start, DateTime end) throws Exception { final String urlExtension = url.get(ot); final int increment = maxIncrement.get(ot); DateTimeComparator comparator = DateTimeComparator.getDateOnlyInstance(); DateTime current = start; // Setup the rate delay if we haven't already Long rateDelay = getRateDelay(updateInfo); try { // Loop from start to end, incrementing by the max number of days you can // specify for a given type of query. This is 1 for burn and sleep, and 31 for steps. // @ loop_invariant date.compareTo(userRegistrationDate) >= 0; while (comparator.compare(current, end) < 0) { if (guestService.getApiKey(updateInfo.apiKey.getId()) == null) { logger.info("Not updating BodyMedia connector instance with a deleted apiKeyId"); return; } String startPeriod = current.toString(formatter); String endPeriod = current.plusDays(increment - 1).toString(formatter); String minutesUrl = "http://api.bodymedia.com/v2/json/" + urlExtension + startPeriod + "/" + endPeriod + "?api_key=" + guestService.getApiKeyAttribute(updateInfo.apiKey, "bodymediaConsumerKey"); // The following call may fail due to bodymedia's api. That is expected behavior enforceRateLimits(rateDelay); String json = signpostHelper.makeRestCall(updateInfo.apiKey, ot.value(), minutesUrl); guestService.setApiKeyAttribute( updateInfo.apiKey, "timeOfLastCall", String.valueOf(System.currentTimeMillis())); JSONObject bodymediaResponse = JSONObject.fromObject(json); JSONArray daysArray = bodymediaResponse.getJSONArray("days"); if (bodymediaResponse.has("lastSync")) { DateTime d = form.parseDateTime(bodymediaResponse.getJSONObject("lastSync").getString("dateTime")); // Get timezone map from UpdateInfo context TimezoneMap tzMap = (TimezoneMap) updateInfo.getContext("tzMap"); // Insert lastSync into the updateInfo context so it's accessible to the updater updateInfo.setContext("lastSync", d); List<AbstractFacet> newFacets = new ArrayList<AbstractFacet>(); for (Object o : daysArray) { if (o instanceof JSONObject) { if (ot == ObjectType.getObjectType(connector(), "steps")) newFacets.add(createOrUpdateStepsFacet((JSONObject) o, updateInfo, d, tzMap)); else if (ot == ObjectType.getObjectType(connector(), "burn")) newFacets.add(createOrUpdateBurnFacet((JSONObject) o, updateInfo, d, tzMap)); else newFacets.add(createOrUpdateSleepFacet((JSONObject) o, updateInfo, d, tzMap)); } } bodyTrackStorageService.storeApiData(updateInfo.getGuestId(), newFacets); } current = current.plusDays(increment); // Update the stored value that controls when we will start updating next time updateStartDate(updateInfo, ot, current); } } catch (Exception e) { StringBuilder sb = new StringBuilder( "module=updateQueue component=updater action=BodymediaUpdater.retrieveHistory") .append(" message=\"exception while retrieving history\" connector=") .append(updateInfo.apiKey.getConnector().toString()) .append(" guestId=") .append(updateInfo.apiKey.getGuestId()) .append(" updatingDate=") .append(current); logger.info(sb.toString()); // Update the stored value that controls when we will start updating next time updateStartDate(updateInfo, ot, current); // Rethrow the error so that this task gets rescheduled throw e; } }
@GET @Path("/installed") @Produces({MediaType.APPLICATION_JSON}) public String getInstalledConnectors() { Guest guest = AuthHelper.getGuest(); // If no guest is logged in, return empty array if (guest == null) return "[]"; ResourceBundle res = ResourceBundle.getBundle("messages/connectors"); try { List<ConnectorInfo> connectors = sysService.getConnectors(); JSONArray connectorsArray = new JSONArray(); for (int i = 0; i < connectors.size(); i++) { final ConnectorInfo connectorInfo = connectors.get(i); final Connector api = connectorInfo.getApi(); if (api == null) { StringBuilder sb = new StringBuilder( "module=API component=connectorStore action=getInstalledConnectors "); logger.warn("message=\"null connector for " + connectorInfo.getName() + "\""); continue; } if (!guestService.hasApiKey(guest.getId(), api) || api.getName().equals("facebook") /*HACK*/) { connectors.remove(i--); } else { ConnectorInfo connector = connectorInfo; JSONObject connectorJson = new JSONObject(); Connector conn = Connector.fromValue(connector.api); ApiKey apiKey = guestService.getApiKey(guest.getId(), conn); connectorJson.accumulate("prettyName", conn.prettyName()); List<String> facetTypes = new ArrayList<String>(); ObjectType[] objTypes = conn.objectTypes(); if (objTypes != null) { for (ObjectType obj : objTypes) { facetTypes.add(connector.connectorName + "-" + obj.getName()); } } connectorJson.accumulate("facetTypes", facetTypes); connectorJson.accumulate( "status", apiKey.status != null ? apiKey.status.toString() : "NA"); connectorJson.accumulate("name", connector.name); connectorJson.accumulate("connectUrl", connector.connectUrl); connectorJson.accumulate("image", connector.image); connectorJson.accumulate("connectorName", connector.connectorName); connectorJson.accumulate("enabled", connector.enabled); connectorJson.accumulate("manageable", connector.manageable); connectorJson.accumulate("text", connector.text); connectorJson.accumulate("api", connector.api); connectorJson.accumulate("apiKeyId", apiKey.getId()); connectorJson.accumulate( "lastSync", connector.supportsSync ? getLastSync(apiKey) : Long.MAX_VALUE); connectorJson.accumulate("latestData", getLatestData(apiKey)); final String auditTrail = checkForErrors(apiKey); connectorJson.accumulate("errors", auditTrail != null); connectorJson.accumulate("auditTrail", auditTrail != null ? auditTrail : ""); connectorJson.accumulate("syncing", checkIfSyncInProgress(guest.getId(), conn)); connectorJson.accumulate( "channels", settingsService.getChannelsForConnector(guest.getId(), conn)); connectorJson.accumulate("sticky", connector.connectorName.equals("fluxtream_capture")); connectorJson.accumulate("supportsRenewToken", connector.supportsRenewTokens); connectorJson.accumulate("supportsSync", connector.supportsSync); connectorJson.accumulate("supportsFileUpload", connector.supportsFileUpload); connectorJson.accumulate("prettyName", conn.prettyName()); final String uploadMessageKey = conn.getName() + ".upload"; if (res.containsKey(uploadMessageKey)) { final String uploadMessage = res.getString(uploadMessageKey); connectorJson.accumulate("uploadMessage", uploadMessage); } connectorsArray.add(connectorJson); } } StringBuilder sb = new StringBuilder("module=API component=connectorStore action=getInstalledConnectors") .append(" guestId=") .append(guest.getId()); logger.info(sb.toString()); return connectorsArray.toString(); } catch (Exception e) { StringBuilder sb = new StringBuilder("module=API component=connectorStore action=getInstalledConnectors") .append(" guestId=") .append(guest.getId()) .append(" stackTrace=<![CDATA[") .append(Utils.stackTrace(e)) .append("]]>"); System.out.println(sb.toString()); logger.warn(sb.toString()); return gson.toJson( new StatusModel(false, "Failed to get installed connectors: " + e.getMessage())); } }