http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/21b60593/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java ---------------------------------------------------------------------- diff --git a/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java b/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java new file mode 100755 index 0000000..61b069f --- /dev/null +++ b/sdks/android/src/main/java/org/apache/usergrid/android/sdk/UGClient.java @@ -0,0 +1,3160 @@ +package org.apache.usergrid.android.sdk; + +import static org.apache.usergrid.android.sdk.utils.ObjectUtils.isEmpty; +import static org.apache.usergrid.android.sdk.utils.UrlUtils.addQueryParams; +import static org.apache.usergrid.android.sdk.utils.UrlUtils.encodeParams; +import static org.apache.usergrid.android.sdk.utils.UrlUtils.path; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Date; + +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationListener; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; + +import org.apache.usergrid.android.sdk.URLConnectionFactory; +import org.apache.usergrid.android.sdk.callbacks.ApiResponseCallback; +import org.apache.usergrid.android.sdk.callbacks.ClientAsyncTask; +import org.apache.usergrid.android.sdk.callbacks.GroupsRetrievedCallback; +import org.apache.usergrid.android.sdk.callbacks.QueryResultsCallback; +import org.apache.usergrid.android.sdk.entities.Activity; +import org.apache.usergrid.android.sdk.entities.Collection; +import org.apache.usergrid.android.sdk.entities.Device; +import org.apache.usergrid.android.sdk.entities.Entity; +import org.apache.usergrid.android.sdk.entities.Group; +import org.apache.usergrid.android.sdk.entities.Message; +import org.apache.usergrid.android.sdk.entities.User; +import org.apache.usergrid.android.sdk.response.ApiResponse; +import org.apache.usergrid.android.sdk.utils.DeviceUuidFactory; +import org.apache.usergrid.android.sdk.utils.JsonUtils; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.DeserializationFeature; + + +/** + * The UGClient class for accessing the Usergrid API. Start by instantiating this + * class though the appropriate constructor. Most calls to the API will be handled + * by the methods in this class. + * + * @see org.apache.usergrid.android.sdk.UGClient + * @see <a href="http://apigee.com/docs/app-services/content/installing-apigee-sdk-android">Usergrid SDK install guide</a> + */ +public class UGClient { + + /** + * Most current version of the Usergrid Android SDK + */ + public static final String SDK_VERSION = "0.0.8"; + /** + * Platform type of this SDK + */ + public static final String SDK_TYPE = "Android"; + + /** + * @y.exclude + */ + public static final String OPTION_KEY_BASE_URL = "baseURL"; + + /** + * @y.exclude + */ + public static boolean FORCE_PUBLIC_API = false; + + /** + * Public API + */ + public static String PUBLIC_API_URL = "https://api.usergrid.com"; + + /** + * Local API of standalone server + */ + public static String LOCAL_STANDALONE_API_URL = "http://localhost:8080"; + + /** + * Local API of Tomcat server in Eclipse + */ + public static String LOCAL_TOMCAT_API_URL = "http://localhost:8080/ROOT"; + + /** + * Local API + */ + public static String LOCAL_API_URL = LOCAL_STANDALONE_API_URL; + + /** + * Standard HTTP methods use in generic request methods + * @see apiRequest + * @see doHttpRequest + */ + protected static final String HTTP_METHOD_DELETE = "DELETE"; + /** + * Standard HTTP methods use in generic request methods + * @see apiRequest + * @see doHttpRequest + */ + protected static final String HTTP_METHOD_GET = "GET"; + /** + * Standard HTTP methods use in generic request methods + * @see apiRequest + * @see doHttpRequest + */ + protected static final String HTTP_METHOD_POST = "POST"; + /** + * Standard HTTP methods use in generic request methods + * @see apiRequest + * @see doHttpRequest + */ + protected static final String HTTP_METHOD_PUT = "PUT"; + + protected static final String LOGGING_TAG = "UGCLIENT"; + + private String apiUrl = PUBLIC_API_URL; + + private String organizationId; + private String applicationId; + private String clientId; + private String clientSecret; + + private User loggedInUser = null; + + private String accessToken = null; + + private String currentOrganization = null; + private URLConnectionFactory urlConnectionFactory = null; + + private LocationManager locationManager; + private UUID deviceID; + + /** + * Interface for EntityQuery and QueueQuery + */ + public interface Query { + + public ApiResponse getResponse(); + + public boolean more(); + + public Query next(); + + } + + /** + * @y.exclude + */ + public static boolean isUuidValid(UUID uuid) { + return( uuid != null ); + } + + protected static String arrayToDelimitedString(String[] arrayOfStrings, String delimiter) { + StringBuilder sb = new StringBuilder(); + + for( int i = 0; i < arrayOfStrings.length; ++i ) { + if( i > 0 ) { + sb.append(delimiter); + } + + sb.append(arrayOfStrings[i]); + } + + return sb.toString(); + } + + protected static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { + if (str == null) { + return null; + } + + StringTokenizer st = new StringTokenizer(str, delimiters); + + int numTokens = st.countTokens(); + List<String> listTokens; + + if( numTokens > 0 ) { + + listTokens = new ArrayList<String>(numTokens); + + while (st.hasMoreTokens()) { + + String token = st.nextToken(); + + if (trimTokens) { + token = token.trim(); + } + + if (!ignoreEmptyTokens || token.length() > 0) { + listTokens.add(token); + } + } + } else { + listTokens = new ArrayList<String>(); + } + + return listTokens.toArray(new String[listTokens.size()]); + } + + + /****************** CONSTRUCTORS ***********************/ + /****************** CONSTRUCTORS ***********************/ + + /** + * @y.exclude + */ + public UGClient() { + init(); + } + + /** + * Instantiate a data client for a specific app. This is used to call most + * SDK methods. + * + * @param organizationId the Usergrid organization name + * @param applicationId the Usergrid application id or name + */ + public UGClient(String organizationId, String applicationId) { + init(); + this.organizationId = organizationId; + this.applicationId = applicationId; + } + + /** + * Instantiate a data client for a specific app with a base URL other than the default + * api.usergrid.com. This is used to call most SDK methods. + * + * @param organizationId the Usergrid organization name + * @param applicationId the Usergrid application id or name + * @param baseURL the base URL to use for all API calls + */ + public UGClient(String organizationId, String applicationId, String baseURL) { + init(); + this.organizationId = organizationId; + this.applicationId = applicationId; + + if( baseURL != null ) { + this.setApiUrl(baseURL); + } + } + + public void init() { + } + + + /****************** ACCESSORS/MUTATORS ***********************/ + /****************** ACCESSORS/MUTATORS ***********************/ + + /** + * Sets a new URLConnectionFactory object in the UGClient + * + * @param urlConnectionFactory a new URLConnectionFactory object + * @y.exclude + */ + public void setUrlConnectionFactory(URLConnectionFactory urlConnectionFactory) { + this.urlConnectionFactory = urlConnectionFactory; + } + + /** + * @return the Usergrid API url (default: http://api.usergrid.com) + */ + public String getApiUrl() { + return apiUrl; + } + + /** + * Sets the base URL for API requests + * + * @param apiUrl the API base url to be set (default: http://api.usergrid.com) + */ + public void setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + } + + /** + * Sets the base URL for API requests and returns the updated UGClient object + * + * @param apiUrl the Usergrid API url (default: http://api.usergrid.com) + * @return UGClient object for method call chaining + */ + public UGClient withApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + return this; + } + + + /** + * Sets the Usergrid organization ID and returns the UGClient object + * + * @param organizationId the organizationId to set + * @return the updated UGClient object + */ + public UGClient withOrganizationId(String organizationId){ + this.organizationId = organizationId; + return this; + } + + + + /** + * Gets the current Usergrid organization ID set in the UGClient + * + * @return the current organizationId + */ + public String getOrganizationId() { + return organizationId; + } + + /** + * Sets the Usergrid organization ID + * + * @param organizationId the organizationId to set + */ + public void setOrganizationId(String organizationId) { + this.organizationId = organizationId; + } + + /** + * Gets the current Usergrid application ID set in the UGClient + * + * @return the current organizationId or name + */ + public String getApplicationId() { + return applicationId; + } + + /** + * Sets the Usergrid application Id + * + * @param applicationId the application id or name + */ + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + + /** + * Sets the Usergrid application ID and returns the UGClient object + * + * @param applicationId the application ID to set + * @return the updated UGClient object + */ + public UGClient withApplicationId(String applicationId) { + this.applicationId = applicationId; + return this; + } + + /** + * Gets the application (not organization) client ID credential for making calls as the + * application-owner. Not safe for most mobile use. + * @return the client id + */ + public String getClientId() { + return clientId; + } + + /** + * Sets the application (not organization) client ID credential, used for making + * calls as the application-owner. Not safe for most mobile use. + * @param clientId the client id + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * Sets the client ID credential in the UGClient object. Not safe for most mobile use. + * + * @param clientId the client key id + * @return UGClient object for method call chaining + */ + public UGClient withClientId(String clientId) { + this.clientId = clientId; + return this; + } + + /** + * Gets the application (not organization) client secret credential for making calls as the + * application-owner. Not safe for most mobile use. + * @return the client secret + */ + public String getClientSecret() { + return clientSecret; + } + + /** + * Sets the application (not organization) client secret credential, used for making + * calls as the application-owner. Not safe for most mobile use. + * + * @param clientSecret the client secret + */ + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + /** + * Sets the client secret credential in the UGClient object. Not safe for most mobile use. + * + * @param clientSecret the client secret + * @return UGClient object for method call chaining + */ + public UGClient withClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + /** + * Gets the UUID of the logged-in user after a successful authorizeAppUser request + * @return the UUID of the logged-in user + */ + public User getLoggedInUser() { + return loggedInUser; + } + + /** + * Sets the UUID of the logged-in user. Usually not set by host application + * @param loggedInUser the UUID of the logged-in user + */ + public void setLoggedInUser(User loggedInUser) { + this.loggedInUser = loggedInUser; + } + + /** + * Gets the OAuth2 access token for the current logged-in user after a + * successful authorize request + * + * @return the OAuth2 access token + */ + public String getAccessToken() { + return accessToken; + } + + /** + * Saves the OAuth2 access token in the UGClient after a successful authorize + * request. Usually not set by host application. + * + * @param accessToken an OAuth2 access token + */ + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + /** + * Gets the current organization from UGClient + * + * @return the currentOrganization + */ + public String getCurrentOrganization() { + return currentOrganization; + } + + /** + * Sets the current organizanization from UGClient + * + * @param currentOrganization The organization this data client should use. + */ + public void setCurrentOrganization(String currentOrganization) { + this.currentOrganization = currentOrganization; + } + + /****************** LOGGING ***********************/ + /****************** LOGGING ***********************/ + + + /** + * Logs a trace-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void logTrace(String logMessage) { + if( logMessage != null ) { + Log.v(LOGGING_TAG,logMessage); + } + } + + /** + * Logs a debug-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void logDebug(String logMessage) { + if( logMessage != null ) { + Log.d(LOGGING_TAG,logMessage); + } + } + + /** + * Logs an info-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void logInfo(String logMessage) { + if( logMessage != null ) { + Log.i(LOGGING_TAG,logMessage); + } + } + + /** + * Logs a warn-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void logWarn(String logMessage) { + if( logMessage != null ) { + Log.w(LOGGING_TAG,logMessage); + } + } + + /** + * Logs an error-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void logError(String logMessage) { + if( logMessage != null ) { + Log.e(LOGGING_TAG,logMessage); + } + } + + /** + * Logs a debug-level logging message with tag 'DATA_CLIENT' + * + * @param logMessage the message to log + */ + public void writeLog(String logMessage) { + if( logMessage != null ) { + //TODO: do we support different log levels in this class? + Log.d(LOGGING_TAG, logMessage); + } + } + + /****************** API/HTTP REQUEST ***********************/ + /****************** API/HTTP REQUEST ***********************/ + + /** + * Forms and initiates a raw synchronous http request and processes the response. + * + * @param httpMethod the HTTP method in the format: + * HTTP_METHOD_<method_name> (e.g. HTTP_METHOD_POST) + * @param params the URL parameters to append to the request URL + * @param data the body of the request + * @param segments additional URL path segments to append to the request URL + * @return ApiResponse object + */ + public ApiResponse doHttpRequest(String httpMethod, Map<String, Object> params, Object data, String... segments) { + + ApiResponse response = null; + OutputStream out = null; + InputStream in = null; + HttpURLConnection conn = null; + + String urlAsString = path(apiUrl, segments); + + try { + String contentType = "application/json"; + if (httpMethod.equals(HTTP_METHOD_POST) && isEmpty(data) && !isEmpty(params)) { + data = encodeParams(params); + contentType = "application/x-www-form-urlencoded"; + } else { + urlAsString = addQueryParams(urlAsString, params); + } + + //logTrace("Invoking " + httpMethod + " to '" + urlAsString + "'"); + + URL url = new URL(urlAsString); + conn = (HttpURLConnection) url.openConnection(); + + conn.setRequestMethod(httpMethod); + conn.setRequestProperty("Content-Type", contentType); + conn.setUseCaches(false); + + if ((accessToken != null) && (accessToken.length() > 0)) { + String authStr = "Bearer " + accessToken; + conn.setRequestProperty("Authorization", authStr); + } + + conn.setDoInput(true); + + if (httpMethod.equals(HTTP_METHOD_POST) || httpMethod.equals(HTTP_METHOD_PUT)) { + if (isEmpty(data)) { + data = JsonNodeFactory.instance.objectNode(); + } + + String dataAsString = null; + + if ((data != null) && (!(data instanceof String))) { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + dataAsString = objectMapper.writeValueAsString(data); + } else { + dataAsString = (String) data; + } + + //logTrace("Posting/putting data: '" + dataAsString + "'"); + + byte[] dataAsBytes = dataAsString.getBytes(); + + conn.setRequestProperty("Content-Length", Integer.toString(dataAsBytes.length)); + conn.setDoOutput(true); + + out = conn.getOutputStream(); + out.write(dataAsBytes); + out.flush(); + out.close(); + out = null; + } + + in = conn.getInputStream(); + if( in != null ) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder sb = new StringBuilder(); + String line; + + while( (line = reader.readLine()) != null ) { + sb.append(line); + sb.append('\n'); + } + + String responseAsString = sb.toString(); + + //logTrace("response from server: '" + responseAsString + "'"); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + response = (ApiResponse) objectMapper.readValue(responseAsString, ApiResponse.class); + response.setRawResponse(responseAsString); + + response.setUGClient(this); + } else { + response = null; + logTrace("no response body from server"); + } + + //final int responseCode = conn.getResponseCode(); + //logTrace("responseCode from server = " + responseCode); + } + catch(Exception e) { + logError("Error " + httpMethod + " to '" + urlAsString + "'" ); + if( e != null ) { + e.printStackTrace(); + logError(e.getLocalizedMessage()); + } + response = null; + } + catch(Throwable t) { + logError("Error " + httpMethod + " to '" + urlAsString + "'" ); + if( t != null ) { + t.printStackTrace(); + logError(t.getLocalizedMessage()); + } + response = null; + } + finally { + try { + if( out != null ) { + out.close(); + } + + if( in != null ) { + in.close(); + } + + if( conn != null ) { + conn.disconnect(); + } + } catch(Exception ignored) { + } + } + + return response; + } + + + /** + * High-level synchronous API request. Implements the http request + * for most SDK methods by calling + * {@link #doHttpRequest(String,Map,Object,String...)} + * + * @param httpMethod the HTTP method in the format: + * HTTP_METHOD_<method_name> (e.g. HTTP_METHOD_POST) + * @param params the URL parameters to append to the request URL + * @param data the body of the request + * @param segments additional URL path segments to append to the request URL + * @return ApiResponse object + */ + public ApiResponse apiRequest(String httpMethod, + Map<String, Object> params, Object data, String... segments) { + ApiResponse response = null; + + response = doHttpRequest(httpMethod, params, data, segments); + + if( (response == null) ) { + logError("doHttpRequest returned null"); + } + + return response; + } + + protected void assertValidApplicationId() { + if (isEmpty(applicationId)) { + throw new IllegalArgumentException("No application id specified"); + } + } + + /****************** ROLES/PERMISSIONS ***********************/ + /****************** ROLES/PERMISSIONS ***********************/ + + /** + * Assigns permissions to the specified user, group, or role. + * + * @param entityType the entity type of the entity the permissions are being assigned to. 'user', 'group' and 'role' are valid. + * @param entityID the UUID of 'name' property of the entity the permissions are being assigned to. + * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method + * @return ApiResponse object + */ + public ApiResponse assignPermissions(String entityType, String entityID, String permissions) { + + if (!entityType.substring(entityType.length() - 1 ).equals("s")) { + entityType += "s"; + } + + if (!validateTypeForPermissionsAndRoles(entityType, "permission")) { + throw new IllegalArgumentException("Permissions can only be assigned to group, user, or role entities"); + } + + Map<String, Object> data = new HashMap<String, Object>(); + if (permissions != null){ + data.put("permission", permissions); + } + + return apiRequest(HTTP_METHOD_POST, null, data, organizationId, applicationId, entityType, + entityID, "permissions"); + + } + + /** + * Assigns permissions to the specified user, group, or role. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param entityType the entity type of the entity the permissions are being assigned to. 'user', 'group' and 'role' are valid. + * @param entityID the UUID of 'name' property of the entity the permissions are being assigned to. + * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @param callback an ApiResponseCallback to handle the async response + */ + public void assignPermissionsAsync(final String entityType, + final String entityID, final String permissions, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return assignPermissions(entityType, entityID, permissions); + } + }).execute(); + } + + /** + * Removes permissions from the specified user, group or role. + * + * @param entityType the entity type of the entity the permissions are being removed from. 'user', 'group' and 'role' are valid. + * @param entityID the UUID of 'name' property of the entity the permissions are being removed from. + * @param permissions a comma-separated list of the permissions to be removed in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method + * @return ApiResponse object + */ + public ApiResponse removePermissions(String entityType, String entityID, String permissions) { + + if (!validateTypeForPermissionsAndRoles(entityType, "permission")) { + throw new IllegalArgumentException("Permissions can only be assigned to group, user, or role entities"); + } + + Map<String, Object> params = new HashMap<String, Object>(); + if (permissions != null){ + params.put("permission", permissions); + } + + return apiRequest(HTTP_METHOD_DELETE, params, null, organizationId, applicationId, entityType, + entityID, "permissions"); + + } + + /** + * Removes permissions from the specified user, group or role. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param entityType the entity type of the entity the permissions are being removed from. 'user', 'group', and 'role' are valid. + * @param entityID the UUID of 'name' property of the entity the permissions are being removed from. + * @param permissions a comma-separated list of the permissions to be removed in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @param callback an ApiResponseCallback to handle the async response + */ + public void removePermissionsAsync(final String entityType, + final String entityID, final String permissions, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return removePermissions(entityType, entityID, permissions); + } + }).execute(); + } + + /** + * Creates a new role and assigns permissions to it. + * + * @param roleName the name of the new role + * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @return ApiResponse object + */ + public ApiResponse createRole(String roleName, String permissions) { + + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put("type", "role"); + properties.put("name", roleName); + + ApiResponse response = this.createEntity(properties); + + String uuid = null; + + if (response.getEntityCount() == 1){ + uuid = response.getFirstEntity().getUuid().toString(); + } + + return assignPermissions("role", uuid, permissions); + + } + + /** + * Creates a new role and assigns permissions to it. + * + * @param roleName the name of the new role + * @param permissions a comma-separated list of the permissions to be assigned in the format: <operations>:<path>, e.g. get, put, post, delete: /users + * @param callback an ApiResponseCallback to handle the async response + */ + public void createRoleAsync(final String roleName, final String permissions, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createRole(roleName, permissions); + } + }).execute(); + } + + /** + * Assigns a role to a user or group entity. + * + * @param roleName the name of the role to be assigned to the entity + * @param entityType the entity type of the entity the role is being assigned to. 'user' and 'group' are valid. + * @param entityID the UUID or 'name' property of the entity the role is being assigned to. + * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method + * @return ApiResponse object + */ + public ApiResponse assignRole(String roleName, String entityType, String entityID) { + + if (!entityType.substring(entityType.length() - 1 ).equals("s")) { + entityType += "s"; + } + + if (!validateTypeForPermissionsAndRoles(entityType, "role")) { + throw new IllegalArgumentException("Permissions can only be assigned to a group or user"); + } + + return apiRequest(HTTP_METHOD_POST, null, null, organizationId, applicationId, "roles", roleName, + entityType, entityID); + + } + + /** + * Assigns a role to a user or group entity. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param roleName the name of the role to be assigned to the entity + * @param entityType the entity type of the entity the role is being assigned to. 'user' and 'group' are valid. + * @param entityID the UUID or 'name' property of the entity the role is being removed from. + * @param callback an ApiResponseCallback to handle the async response + */ + public void assignRoleAsync(final String roleName, final String entityType, + final String entityID, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return assignRole(roleName, entityType, entityID); + } + }).execute(); + } + + /** + * Removes a role from a user or group entity. + * + * @param roleName the name of the role to be removed from the entity + * @param entityType the entity type of the entity the role is being removed from. 'user' and 'group' are valid. + * @param entityID the UUID or 'name' property of the entity the role is being removed from. + * @throws IllegalArgumentException thrown if an entityType other than 'group' or 'user' is passed to the method + * @return ApiResponse object + */ + public ApiResponse removeRole(String roleName, String entityType, String entityID) { + + if (!entityType.substring(entityType.length() - 1 ).equals("s")) { + entityType += "s"; + } + + if (!validateTypeForPermissionsAndRoles(entityType, "role")) { + throw new IllegalArgumentException("Permissions can only be removed from a group or user"); + } + + return apiRequest(HTTP_METHOD_DELETE, null, null, organizationId, applicationId, "roles", roleName, + entityType, entityID); + + } + + /** + * Removes a role from a user or group entity. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param roleName the name of the role to be removed from the entity + * @param entityType the entity type of the entity the role is being removed from. 'user' and 'group' are valid. + * @param entityID the UUID or 'name' property of the entity the role is being removed from. + * @param callback an ApiResponseCallback to handle the async response + */ + public void removeRoleAsync(final String roleName, final String entityType, + final String entityID, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return removeRole(roleName, entityType, entityID); + } + }).execute(); + } + + /** + * Checks if a permission or role can be assigned to an entity + * @y.exclude + */ + private Boolean validateTypeForPermissionsAndRoles(String type, String permissionOrRole){ + ArrayList<String> validTypes = new ArrayList<String>(); + validTypes.add("groups"); + validTypes.add("users"); + + if (permissionOrRole.equals("permission")){ + validTypes.add("roles"); + } + + return validTypes.contains(type); + } + + /****************** LOG IN/LOG OUT/OAUTH ***********************/ + /****************** LOG IN/LOG OUT/OAUTH ***********************/ + + /** + * Logs the user in and get a valid access token. + * + * @param usernameOrEmail the username or email associated with the user entity in Usergrid + * @param password the user's Usergrid password + * @return non-null ApiResponse if request succeeds, check getError() for + * "invalid_grant" to see if access is denied. + */ + public ApiResponse authorizeAppUser(String usernameOrEmail, String password) { + validateNonEmptyParam(usernameOrEmail, "email"); + validateNonEmptyParam(password,"password"); + assertValidApplicationId(); + loggedInUser = null; + accessToken = null; + currentOrganization = null; + Map<String, Object> formData = new HashMap<String, Object>(); + formData.put("grant_type", "password"); + formData.put("username", usernameOrEmail); + formData.put("password", password); + ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null, + organizationId, applicationId, "token"); + if (response == null) { + return response; + } + if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) { + loggedInUser = response.getUser(); + accessToken = response.getAccessToken(); + currentOrganization = null; + logInfo("authorizeAppUser(): Access token: " + accessToken); + } else { + logInfo("authorizeAppUser(): Response: " + response); + } + return response; + } + + /** + * Log the user in and get a valid access token. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param usernameOrEmail the username or email associated with the user entity in Usergrid + * @param password the users Usergrid password + * @param callback an ApiResponseCallback to handle the async response + */ + public void authorizeAppUserAsync(final String usernameOrEmail, + final String password, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return authorizeAppUser(usernameOrEmail, password); + } + }).execute(); + } + + /** + * Change the password for the currently logged in user. You must supply the + * old password and the new password. + * + * @param username the username or email address associated with the user entity in Usergrid + * @param oldPassword the user's old password + * @param newPassword the user's new password + * @return ApiResponse object + */ + public ApiResponse changePassword(String username, String oldPassword, + String newPassword) { + + Map<String, Object> data = new HashMap<String, Object>(); + data.put("newpassword", newPassword); + data.put("oldpassword", oldPassword); + + return apiRequest(HTTP_METHOD_POST, null, data, organizationId, applicationId, "users", + username, "password"); + + } + + public void changePasswordAsync(final String username, final String oldPassword, + final String newPassword, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return changePassword(username, oldPassword, newPassword); + } + }).execute(); + } + + /** + * Log the user in with their numeric pin-code and get a valid access token. + * + * @param email the email address associated with the user entity in Usergrid + * @param pin the pin associated with the user entity in Usergrid + * @return non-null ApiResponse if request succeeds, check getError() for + * "invalid_grant" to see if access is denied. + */ + public ApiResponse authorizeAppUserViaPin(String email, String pin) { + validateNonEmptyParam(email, "email"); + validateNonEmptyParam(pin, "pin"); + assertValidApplicationId(); + loggedInUser = null; + accessToken = null; + currentOrganization = null; + Map<String, Object> formData = new HashMap<String, Object>(); + formData.put("grant_type", "pin"); + formData.put("username", email); + formData.put("pin", pin); + ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null, + organizationId, applicationId, "token"); + if (response == null) { + return response; + } + if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) { + loggedInUser = response.getUser(); + accessToken = response.getAccessToken(); + currentOrganization = null; + logInfo("authorizeAppUser(): Access token: " + accessToken); + } else { + logInfo("authorizeAppUser(): Response: " + response); + } + return response; + } + + /** + * Log the user in with their numeric pin-code and get a valid access token. + * Executes asynchronously in background and the callbacks are called in the + * UI thread. + * + * @param email the email address associated with the user entity in Usergrid + * @param pin the pin associated with the user entity in Usergrid + * @param callback A callback for the async response. + */ + public void authorizeAppUserViaPinAsync(final String email, + final String pin, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return authorizeAppUserViaPin(email, pin); + } + }).execute(); + } + + /** + * Log the app in with it's application (not organization) client id and + * client secret key. Not recommended for production apps. Executes asynchronously + * in background and the callbacks are called in the UI thread. + * + * @param clientId the Usergrid application's client ID + * @param clientSecret the Usergrid application's client secret + * @param callback an ApiResponseCallback to handle the async response + */ + public void authorizeAppClientAsync(final String clientId, + final String clientSecret, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + + @Override + public ApiResponse doTask() { + return authorizeAppClient(clientId, clientSecret); + } + }).execute(); + } + + private void validateNonEmptyParam(Object param, String paramName) { + if ( isEmpty(param) ) { + throw new IllegalArgumentException(paramName + " cannot be null or empty"); + } + } + + /** + * Log the user in with their Facebook access token retrieved via Facebook + * OAuth. Sets the user's identifier and Usergrid OAuth2 access token in UGClient + * if successfully authorized. + * + * @param fb_access_token the valid OAuth token returned by Facebook + * @return non-null ApiResponse if request succeeds, check getError() for + * "invalid_grant" to see if access is denied. + */ + public ApiResponse authorizeAppUserViaFacebook(String fb_access_token) { + validateNonEmptyParam(fb_access_token, "Facebook token"); + assertValidApplicationId(); + loggedInUser = null; + accessToken = null; + currentOrganization = null; + Map<String, Object> formData = new HashMap<String, Object>(); + formData.put("fb_access_token", fb_access_token); + ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null, + organizationId, applicationId, "auth", "facebook"); + if (response == null) { + return response; + } + if (!isEmpty(response.getAccessToken()) && (response.getUser() != null)) { + loggedInUser = response.getUser(); + accessToken = response.getAccessToken(); + currentOrganization = null; + logInfo("authorizeAppUserViaFacebook(): Access token: " + + accessToken); + } else { + logInfo("authorizeAppUserViaFacebook(): Response: " + + response); + } + return response; + } + + /** + * Log the user in with their Facebook access token retrieved via Facebook + * OAuth. Sets the user's identifier and Usergrid OAuth2 access token in UGClient + * if successfully authorized. Executes asynchronously in background and the + * callbacks are called in the UI thread. + * + * @param fb_access_token the valid OAuth token returned by Facebook + * @param callback an ApiResponseCallback to handle the async response + */ + public void authorizeAppUserViaFacebookAsync(final String fb_access_token, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return authorizeAppUserViaFacebook(fb_access_token); + } + }).execute(); + } + + /** + * Log out a user and destroy the access token currently stored in UGClient + * on the server and in the UGClient. + * + * @param username The username to be logged out + * @return non-null ApiResponse if request succeeds + */ + public ApiResponse logOutAppUser(String username) { + String token = getAccessToken(); + Map<String,Object> params = new HashMap<String,Object>(); + params.put("token",token); + ApiResponse response = apiRequest(HTTP_METHOD_PUT, params, null, + organizationId, applicationId, "users",username,"revoketoken?"); + if (response == null) { + return response; + } else { + logInfo("logoutAppUser(): Response: " + response); + setAccessToken(null); + } + return response; + } + + /** + * Log out a user and destroy the access token currently stored in UGClient + * on the server and in the UGClient. + * Executes asynchronously in background and the callbacks are called in the + * UI thread. + * + * @param username The username to be logged out + * @param callback an ApiResponseCallback to handle the async response + */ + public void logOutAppUserAsync(final String username, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return logOutAppUser(username); + } + }).execute(); + } + + /** + * Destroy a specific user token on the server. The token will also be cleared + * from the UGClient instance, if it matches the token provided. + * + * @param username The username to be logged out + * @param token The access token to be destroyed on the server + * @return non-null ApiResponse if request succeeds + */ + public ApiResponse logOutAppUserForToken(String username, String token) { + Map<String,Object> params = new HashMap<String,Object>(); + params.put("token",token); + ApiResponse response = apiRequest(HTTP_METHOD_PUT, params, null, + organizationId, applicationId, "users",username,"revoketoken?"); + if (response == null) { + return response; + } else { + logInfo("logoutAppWithTokenUser(): Response: " + response); + if (token.equals(getAccessToken())) { + setAccessToken(null); + } + } + return response; + } + + /** + * Destroy a specific user token on the server. The token will also be cleared + * from the UGClient instance, if it matches the token provided. + * Executes asynchronously in background and the callbacks are called in the UI thread. + * + * @param username The username to be logged out + * @param token The access token to be destroyed on the server + * @param callback A callback for the async response + */ + public void logOutAppUserForTokenAsync(final String username, final String token, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return logOutAppUserForToken(username, token); + } + }).execute(); + } + + /** + * Log out a user and destroy all associated tokens on the server. + * The token stored in UGClient will also be destroyed. + * + * @param username The username to be logged out + * @return non-null ApiResponse if request succeeds + */ + public ApiResponse logOutAppUserForAllTokens(String username) { + ApiResponse response = apiRequest(HTTP_METHOD_PUT, null, null, + organizationId, applicationId, "users",username,"revoketokens"); + if (response == null) { + return response; + } else { + logInfo("logoutAppUserForAllTokens(): Response: " + response); + setAccessToken(null); + } + return response; + } + + /** + * Log out a user and destroy all associated tokens on the server. + * The token stored in UGClient will also be destroyed. + * Executes asynchronously in background and the callbacks are called in the UI thread. + * + * @param username The username to be logged out + * @param callback A callback for the response + */ + public void logOutAppUserForAllTokensAsync(final String username, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return logOutAppUserForAllTokens(username); + } + }).execute(); + } + + /** + * Log the app in with it's application (not organization) client id and + * client secret key. Not recommended for production apps. + * + * @param clientId the Usergrid application's client ID + * @param clientSecret the Usergrid application's client secret + */ + public ApiResponse authorizeAppClient(String clientId, String clientSecret) { + validateNonEmptyParam(clientId, "client identifier"); + validateNonEmptyParam(clientSecret, "client secret"); + assertValidApplicationId(); + loggedInUser = null; + accessToken = null; + currentOrganization = null; + Map<String, Object> formData = new HashMap<String, Object>(); + formData.put("grant_type", "client_credentials"); + formData.put("client_id", clientId); + formData.put("client_secret", clientSecret); + ApiResponse response = apiRequest(HTTP_METHOD_POST, formData, null, + organizationId, applicationId, "token"); + if (response == null) { + return response; + } + if (!isEmpty(response.getAccessToken())) { + loggedInUser = null; + accessToken = response.getAccessToken(); + currentOrganization = null; + logInfo("authorizeAppClient(): Access token: " + + accessToken); + } else { + logInfo("authorizeAppClient(): Response: " + response); + } + return response; + } + + + /****************** GENERIC ENTITY MANAGEMENT ***********************/ + /****************** GENERIC ENTITY MANAGEMENT ***********************/ + + /** + * Create a new entity on the server. + * + * @param entity + * @return an ApiResponse with the new entity in it. + */ + public ApiResponse createEntity(Entity entity) { + assertValidApplicationId(); + if (isEmpty(entity.getType())) { + throw new IllegalArgumentException("Missing entity type"); + } + ApiResponse response = apiRequest(HTTP_METHOD_POST, null, entity, + organizationId, applicationId, entity.getType()); + return response; + } + + /** + * Create a new entity on the server from a set of properties. Properties + * must include a "type" property. + * + * @param properties + * @return an ApiResponse with the new entity in it. + */ + public ApiResponse createEntity(Map<String, Object> properties) { + assertValidApplicationId(); + if (isEmpty(properties.get("type"))) { + throw new IllegalArgumentException("Missing entity type"); + } + ApiResponse response = apiRequest(HTTP_METHOD_POST, null, properties, + organizationId, applicationId, properties.get("type").toString()); + return response; + } + + /** + * Create a new entity on the server. Executes asynchronously in background + * and the callbacks are called in the UI thread. + * + * @param entity An instance with data to use to create the entity + * @param callback A callback for the async response + */ + public void createEntityAsync(final Entity entity, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createEntity(entity); + } + }).execute(); + } + + + /** + * Create a new entity on the server from a set of properties. Properties + * must include a "type" property. Executes asynchronously in background and + * the callbacks are called in the UI thread. + * + * @param properties The values to use, with keys as property names and values + * as property values + * @param callback A callback for the async response + */ + public void createEntityAsync(final Map<String, Object> properties, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createEntity(properties); + } + }).execute(); + } + + /** + * Create a set of entities on the server from an ArrayList. Each item in the array + * contains a set of properties that define a entity. + * + * @param type The type of entities to create. + * @param entities A list of maps where keys are entity property names and values + * are property values. + * @return An instance with response data from the server. + */ + public ApiResponse createEntities(String type, ArrayList<Map<String, Object>> entities) { + assertValidApplicationId(); + if (isEmpty(type)) { + throw new IllegalArgumentException("Missing entity type"); + } + ApiResponse response = apiRequest(HTTP_METHOD_POST, null, entities, + organizationId, applicationId, type); + return response; + } + + /** + * Create a set of entities on the server from an ArrayList. Each item in the array + * contains a set of properties that define a entity. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param type The type of entities to create. + * @param entities A list of maps where keys are entity property names and values + * are property values. + * @param callback A callback for the async response + */ + public void createEntitiesAsync(final String type, final ArrayList<Map<String, Object>> entities, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createEntities(type, entities); + } + }).execute(); + } + + /** + * Creates an object instance that corresponds to the provided entity type. + * Supported object types are Activity, Device, Group, Message, and User. + * All other types will return a generic Entity instance with no type assigned. + * + * @param type the entity type of which to create an object instance + * @return an object instance that corresponds to the type provided + */ + public Entity createTypedEntity(String type) { + Entity entity = null; + + if( Activity.isSameType(type) ) { + entity = new Activity(this); + } else if( Device.isSameType(type) ) { + entity = new Device(this); + } else if( Group.isSameType(type) ) { + entity = new Group(this); + } else if( Message.isSameType(type) ) { + entity = new Message(this); + } else if( User.isSameType(type) ) { + entity = new User(this); + } else { + entity = new Entity(this); + } + + return entity; + } + + /** + * Requests all entities of specified type that match the provided query string. + * + * @param type the entity type to be retrieved + * @param queryString a query string to send with the request + * @return a non-null ApiResponse object if successful + */ + public ApiResponse getEntities(String type,String queryString) + { + Map<String, Object> params = null; + + if (queryString.length() > 0) { + params = new HashMap<String, Object>(); + params.put("ql", queryString); + } + + return apiRequest(HTTP_METHOD_GET, // method + params, // params + null, // data + organizationId, + applicationId, + type); + } + + /** + * Asynchronously requests all entities of specified type that match the provided query string. + * + * @param type the entity type to be retrieved + * @param queryString a query string to send with the request + * @param callback an ApiResponseCallback to handle the async response + */ + public void getEntitiesAsync(final String type, + final String queryString, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return getEntities(type, queryString); + } + }).execute(); + } + + /** + * Update an existing entity on the server. + * + * @param entityID the entity to update + * @param updatedProperties the new properties + * @return an ApiResponse with the updated entity in it. + */ + public ApiResponse updateEntity(String entityID, Map<String, Object> updatedProperties) { + assertValidApplicationId(); + if (isEmpty(updatedProperties.get("type"))) { + throw new IllegalArgumentException("Missing entity type"); + } + ApiResponse response = apiRequest(HTTP_METHOD_PUT, null, updatedProperties, + organizationId, applicationId, updatedProperties.get("type").toString(), entityID); + return response; + } + + + /** + * Update an existing entity on the server. Properties + * must include a "type" property. Executes asynchronously in background and + * the callbacks are called in the UI thread. + * + * @param entityID the entity to update + * @param updatedProperties the new properties + * @param callback A callback for the async response + */ + public void updateEntityAsync(final String entityID, final Map<String, Object> updatedProperties, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return updateEntity(entityID, updatedProperties); + } + }).execute(); + } + + /** + * Updates the password associated with a user entity + * + * @param usernameOrEmail the username or email address associated with the user entity + * @param oldPassword the user's old password + * @param newPassword the user's new password + * @return an ApiResponse with the updated entity in it. + */ + public ApiResponse updateUserPassword(String usernameOrEmail, String oldPassword, String newPassword) { + Map<String,Object> updatedProperties = new HashMap<String,Object>(); + updatedProperties.put("oldpassword", oldPassword); + updatedProperties.put("newpassword", newPassword); + return apiRequest(HTTP_METHOD_POST, null, updatedProperties, + organizationId, applicationId, "users", usernameOrEmail); + } + + + /** + * Remove an existing entity on the server. + * + * @param entityType the collection of the entity + * @param entityID the specific entity to delete + * @return an ApiResponse indicating whether the removal was successful + */ + public ApiResponse removeEntity(String entityType, String entityID) { + assertValidApplicationId(); + if (isEmpty(entityType)) { + throw new IllegalArgumentException("Missing entity type"); + } + ApiResponse response = apiRequest(HTTP_METHOD_DELETE, null, null, + organizationId, applicationId, entityType, entityID); + return response; + } + + /** + * Remove an existing entity on the server. + * Executes asynchronously in background and + * the callbacks are called in the UI thread. + * + * @param entityType the collection of the entity + * @param entityID the specific entity to delete + * @param callback A callback with the async response + */ + public void removeEntityAsync(final String entityType, final String entityID, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return removeEntity(entityType, entityID); + } + }).execute(); + } + + /** + * Perform a query request and return a query object. The Query object + * provides a simple way of dealing with result sets that need to be + * iterated or paged through. + * + * See {@link #doHttpRequest(String,Map,Object,String...)} for + * more on the parameters. + * + * @param httpMethod The HTTP method to use in the query + * @param params Query parameters. + * @param data The request body. + * @param segments Additional URL path segments to append to the request URL + * @return An instance representing query results + */ + public Query queryEntitiesRequest(String httpMethod, + Map<String, Object> params, Object data, String... segments) { + ApiResponse response = apiRequest(httpMethod, params, data, segments); + return new EntityQuery(response, httpMethod, params, data, segments); + } + + /** + * Perform a query request and return a query object. The Query object + * provides a simple way of dealing with result sets that need to be + * iterated or paged through. Executes asynchronously in background and the + * callbacks are called in the UI thread. + * + * See {@link #doHttpRequest(String,Map,Object,String...)} for + * more on the parameters. + * + * @param callback A callback for the async response + * @param httpMethod The HTTP method to use in the query + * @param params Query parameters. + * @param data The request body. + * @param segments Additional URL path segments to append to the request URL + */ + public void queryEntitiesRequestAsync(final QueryResultsCallback callback, + final String httpMethod, final Map<String, Object> params, + final Object data, final String... segments) { + (new ClientAsyncTask<Query>(callback) { + @Override + public Query doTask() { + return queryEntitiesRequest(httpMethod, params, data, segments); + } + }).execute(); + } + + /** + * Query object for handling the response from certain query requests + * @y.exclude + */ + private class EntityQuery implements Query { + final String httpMethod; + final Map<String, Object> params; + final Object data; + final String[] segments; + final ApiResponse response; + + private EntityQuery(ApiResponse response, String httpMethod, + Map<String, Object> params, Object data, String[] segments) { + this.response = response; + this.httpMethod = httpMethod; + this.params = params; + this.data = data; + this.segments = segments; + } + + private EntityQuery(ApiResponse response, EntityQuery q) { + this.response = response; + httpMethod = q.httpMethod; + params = q.params; + data = q.data; + segments = q.segments; + } + + /** + * Gets the API response from the last request + * + * @return an ApiResponse object + */ + public ApiResponse getResponse() { + return response; + } + + /** + * Checks if there are more results available based on whether a + * 'cursor' property was present in the last result set. + * + * @return true if the server indicates more results are available + */ + public boolean more() { + if ((response != null) && (response.getCursor() != null) + && (response.getCursor().length() > 0)) { + return true; + } + return false; + } + + /** + * Performs a request for the next set of results based on the cursor + * from the last result set. + * + * @return query that contains results and where to get more. + */ + public Query next() { + if (more()) { + Map<String, Object> nextParams = null; + if (params != null) { + nextParams = new HashMap<String, Object>(params); + } else { + nextParams = new HashMap<String, Object>(); + } + nextParams.put("cursor", response.getCursor()); + ApiResponse nextResponse = apiRequest(httpMethod, nextParams, data, + segments); + return new EntityQuery(nextResponse, this); + } + return null; + } + + } + + + /****************** USER ENTITY MANAGEMENT ***********************/ + /****************** USER ENTITY MANAGEMENT ***********************/ + + /** + * Creates a user entity. + * + * @param username required. The username to be associated with the user entity. + * @param name the user's full name. Can be null. + * @param email the user's email address. + * @param password the user's password + * @return an ApiResponse object + */ + public ApiResponse createUser(String username, String name, String email, + String password) { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put("type", "user"); + if (username != null) { + properties.put("username", username); + } + if (name != null) { + properties.put("name", name); + } + if (email != null) { + properties.put("email", email); + } + if (password != null) { + properties.put("password", password); + } + return createEntity(properties); + } + + /** + * Creates a user. Executes asynchronously in background and the callbacks + * are called in the UI thread. + * + * @param username required. The username to be associated with the user entity. + * @param name the user's full name. Can be null. + * @param email the user's email address. + * @param password the user's password. + * @param callback an ApiResponse callback for handling the async response. + */ + public void createUserAsync(final String username, final String name, + final String email, final String password, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createUser(username, name, email, password); + } + }).execute(); + } + + /** + * Retrieves the /users collection. + * + * @return a Query object + */ + public Query queryUsers() { + Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, + organizationId, applicationId, "users"); + return q; + } + + /** + * Retrieves the /users collection. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param callback a QueryResultsCallback object to handle the async response + */ + public void queryUsersAsync(QueryResultsCallback callback) { + queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, null, null, + organizationId, applicationId, "users"); + } + + + /** + * Performs a query of the users collection using the provided query command. + * For example: "name contains 'ed'". + * + * @param ql the query to execute + * @return a Query object + */ + public Query queryUsers(String ql) { + Map<String, Object> params = new HashMap<String, Object>(); + params.put("ql", ql); + Query q = queryEntitiesRequest(HTTP_METHOD_GET, params, null,organizationId, + applicationId, "users"); + return q; + } + + /** + * Perform a query of the users collection using the provided query command. + * For example: "name contains 'ed'". Executes asynchronously in background + * and the callbacks are called in the UI thread. + * + * @param ql the query to execute + * @param callback a QueryResultsCallback object to handle the async response + */ + public void queryUsersAsync(String ql, QueryResultsCallback callback) { + Map<String, Object> params = new HashMap<String, Object>(); + params.put("ql", ql); + queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, params, null, + organizationId, applicationId, "users"); + } + + /** + * Perform a query of the users collection within the specified distance of + * the specified location and optionally using the provided additional query. + * For example: "name contains 'ed'". + * + * @param distance distance from the location in meters + * @param latitude the latitude of the location to measure from + * @param longitude the longitude of the location to measure from + * @param ql an optional additional query to send with the request + * @return a Query object + */ + public Query queryUsersWithinLocation(float distance, float latitude, + float longitude, String ql) { + Map<String, Object> params = new HashMap<String, Object>(); + params.put("ql", + this.makeLocationQL(distance, latitude, longitude, ql)); + Query q = queryEntitiesRequest(HTTP_METHOD_GET, params, null, organizationId, + applicationId, "users"); + return q; + } + + + /****************** GROUP ENTITY MANAGEMENT ***********************/ + /****************** GROUP ENTITY MANAGEMENT ***********************/ + + /** + * Creates a group with the specified group path. Group paths can be slash + * ("/") delimited like file paths for hierarchical group relationships. + * + * @param groupPath the path to use for the new group. + * @return an ApiResponse object + */ + public ApiResponse createGroup(String groupPath) { + return createGroup(groupPath, null); + } + + /** + * Creates a group with the specified group path and group title. Group + * paths can be slash ("/") delimited like file paths for hierarchical group + * relationships. + * + * @param groupPath the path to use for the new group + * @param groupTitle the title to use for the new group + * @return an ApiResponse object + */ + public ApiResponse createGroup(String groupPath, String groupTitle) { + return createGroup(groupPath, groupTitle, null); + } + + /** + * Create a group with a path, title and name. Group + * paths can be slash ("/") delimited like file paths for hierarchical group + * relationships. + * + * @param groupPath the path to use for the new group + * @param groupTitle the title to use for the new group + * @param groupName the name to use for the new group + * @return an ApiResponse object + */ + public ApiResponse createGroup(String groupPath, String groupTitle, String groupName){ + Map<String, Object> data = new HashMap<String, Object>(); + data.put("type", "group"); + data.put("path", groupPath); + + if (groupTitle != null) { + data.put("title", groupTitle); + } + + if(groupName != null){ + data.put("name", groupName); + } + + return apiRequest(HTTP_METHOD_POST, null, data, organizationId, applicationId, "groups"); + } + + /** + * Creates a group with the specified group path. Group paths can be slash + * ("/") delimited like file paths for hierarchical group relationships. + * Executes asynchronously in background and the callbacks are called in the + * UI thread. + * + * @param groupPath the path to use for the new group. + * @param callback an ApiResponseCallback object to handle the async response + */ + public void createGroupAsync(String groupPath, + final ApiResponseCallback callback) { + createGroupAsync(groupPath, null, null); + } + + /** + * Creates a group with the specified group path and group title. Group + * paths can be slash ("/") deliminted like file paths for hierarchical + * group relationships. Executes asynchronously in background and the + * callbacks are called in the UI thread. + * + * @param groupPath the path to use for the new group. + * @param groupTitle the title to use for the new group. + * @param callback an ApiResponseCallback object to handle the async response + */ + public void createGroupAsync(final String groupPath, + final String groupTitle, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return createGroup(groupPath, groupTitle); + } + }).execute(); + } + + /** + * Gets the groups associated with a user entity + * + * @param userId the UUID of the user entity + * @return a map with the group path as the key and a Group object as the value + */ + public Map<String, Group> getGroupsForUser(String userId) { + ApiResponse response = apiRequest(HTTP_METHOD_GET, null, null, + organizationId, applicationId, "users", userId, "groups"); + Map<String, Group> groupMap = new HashMap<String, Group>(); + if (response != null) { + List<Group> groups = response.getEntities(Group.class); + for (Group group : groups) { + groupMap.put(group.getPath(), group); + } + } + return groupMap; + } + + /** + * Gets the groups associated with a user entity. Executes asynchronously in background and + * the callbacks are called in the UI thread. + * + * @param userId the UUID of the user entity + * @param callback a GroupsRetrievedCallback object to handle the async response + */ + public void getGroupsForUserAsync(final String userId, + final GroupsRetrievedCallback callback) { + (new ClientAsyncTask<Map<String, Group>>(callback) { + @Override + public Map<String, Group> doTask() { + return getGroupsForUser(userId); + } + }).execute(); + } + + /** + * Gets the user entities associated with the specified group. + * + * @param groupId UUID of the group entity + * @return a Query object with the results of the query + */ + public Query queryUsersForGroup(String groupId) { + Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, organizationId, + applicationId, "groups", groupId, "users"); + return q; + } + + /** + * Gets the user entities associated with the specified group. Executes + * asynchronously in background and the callbacks are called in the UI thread. + * + * @param groupId UUID of the group entity + * @param callback a QueryResultsCallback object to handle the async response + */ + public void queryUsersForGroupAsync(String groupId, + QueryResultsCallback callback) { + queryEntitiesRequestAsync(callback, HTTP_METHOD_GET, null, null, + getApplicationId(), "groups", groupId, "users"); + } + + /** + * Connects a user entity to the specified group entity. + * + * @param userId UUID of the user entity + * @param groupId UUID of the group entity + * @return an ApiResponse object + */ + public ApiResponse addUserToGroup(String userId, String groupId) { + return apiRequest(HTTP_METHOD_POST, null, null, organizationId, applicationId, "groups", + groupId, "users", userId); + } + + /** + * Connects a user entity to the specified group entity. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param userId UUID of the user entity + * @param groupId UUID of the group entity + * @param callback an ApiResponseCallback object to handle the async response + */ + public void addUserToGroupAsync(final String userId, final String groupId, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return addUserToGroup(userId, groupId); + } + }).execute(); + } + + /** + * Disconnects a user entity from the specified group entity. + * + * @param userId UUID of the user entity + * @param groupId UUID of the group entity + * @return an ApiResponse object + */ + public ApiResponse removeUserFromGroup(String userId, String groupId) { + return apiRequest(HTTP_METHOD_DELETE, null, null, organizationId, applicationId, "groups", + groupId, "users", userId); + } + + /** + * Disconnects a user entity from the specified group entity. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param userId UUID of the user entity + * @param groupId UUID of the group entity + * @param callback an ApiResponseCallback object to handle the async response + */ + public void removeUserFromGroupAsync(final String userId, final String groupId, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return removeUserFromGroup(userId, groupId); + } + }).execute(); + } + + /****************** ACTIVITY ENTITY MANAGEMENT ***********************/ + /****************** ACTIVITY ENTITY MANAGEMENT ***********************/ + + /** + * Get a user's activity feed. Returned as a query to ease paging. + * + * @param userId UUID of user entity + * @return a Query object + */ + public Query queryActivityFeedForUser(String userId) { + Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, + organizationId, applicationId, "users", userId, "feed"); + return q; + } + + /** + * Get a user's activity feed. Returned as a query to ease paging. Executes + * asynchronously in background and the callbacks are called in the UI + * thread. + * + * + * @param userId UUID of user entity + * @param callback a QueryResultsCallback object to handle the async response + */ + public void queryActivityFeedForUserAsync(final String userId, final QueryResultsCallback callback) { + (new ClientAsyncTask<Query>(callback) { + @Override + public Query doTask() { + return queryActivityFeedForUser(userId); + } + }).execute(); + } + + + /** + * Posts an activity to a user entity's activity stream. Activity must already be created. + * + * @param userId + * @param activity + * @return An instance with the server response + */ + public ApiResponse postUserActivity(String userId, Activity activity) { + return apiRequest(HTTP_METHOD_POST, null, activity, organizationId, applicationId, "users", + userId, "activities"); + } + + /** + * Creates and posts an activity to a user entity's activity stream. + * + * @param verb + * @param title + * @param content + * @param category + * @param user + * @param object + * @param objectType + * @param objectName + * @param objectContent + * @return + */ + public ApiResponse postUserActivity(String verb, String title, + String content, String category, User user, Entity object, + String objectType, String objectName, String objectContent) { + Activity activity = Activity.newActivity(this, verb, title, content, + category, user, object, objectType, objectName, objectContent); + return postUserActivity(user.getUuid().toString(), activity); + } + + /** + * Creates and posts an activity to a user. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param verb + * @param title + * @param content + * @param category + * @param user + * @param object + * @param objectType + * @param objectName + * @param objectContent + * @param callback + */ + public void postUserActivityAsync(final String verb, final String title, + final String content, final String category, final User user, + final Entity object, final String objectType, + final String objectName, final String objectContent, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return postUserActivity(verb, title, content, category, user, + object, objectType, objectName, objectContent); + } + }).execute(); + } + + /** + * Posts an activity to a group. Activity must already be created. + * + * @param groupId + * @param activity + * @return + */ + public ApiResponse postGroupActivity(String groupId, Activity activity) { + return apiRequest(HTTP_METHOD_POST, null, activity, organizationId, applicationId, "groups", + groupId, "activities"); + } + + /** + * Creates and posts an activity to a group. + * + * @param groupId + * @param verb + * @param title + * @param content + * @param category + * @param user + * @param object + * @param objectType + * @param objectName + * @param objectContent + * @return + */ + public ApiResponse postGroupActivity(String groupId, String verb, String title, + String content, String category, User user, Entity object, + String objectType, String objectName, String objectContent) { + Activity activity = Activity.newActivity(this, verb, title, content, + category, user, object, objectType, objectName, objectContent); + return postGroupActivity(groupId, activity); + } + + /** + * Creates and posts an activity to a group. Executes asynchronously in + * background and the callbacks are called in the UI thread. + * + * @param groupId + * @param verb + * @param title + * @param content + * @param category + * @param user + * @param object + * @param objectType + * @param objectName + * @param objectContent + * @param callback + */ + public void postGroupActivityAsync(final String groupId, final String verb, final String title, + final String content, final String category, final User user, + final Entity object, final String objectType, + final String objectName, final String objectContent, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return postGroupActivity(groupId, verb, title, content, category, user, + object, objectType, objectName, objectContent); + } + }).execute(); + } + + /** + * Post an activity to the stream. + * + * @param activity + * @return + */ + public ApiResponse postActivity(Activity activity) { + return createEntity(activity); + } + + /** + * Creates and posts an activity to a group. + * + * @param verb + * @param title + * @param content + * @param category + * @param user + * @param object + * @param objectType + * @param objectName + * @param objectContent + * @return + */ + public ApiResponse postActivity(String verb, String title, + String content, String category, User user, Entity object, + String objectType, String objectName, String objectContent) { + Activity activity = Activity.newActivity(this, verb, title, content, + category, user, object, objectType, objectName, objectContent); + return createEntity(activity); + } + + /** + * Get a group's activity feed. Returned as a query to ease paging. + * + * @return + */ + public Query queryActivity() { + Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, + organizationId, applicationId, "activities"); + return q; + } + + + + /** + * Get a group's activity feed. Returned as a query to ease paging. + * + * @param groupId + * @return + */ + public Query queryActivityFeedForGroup(String groupId) { + Query q = queryEntitiesRequest(HTTP_METHOD_GET, null, null, + organizationId, applicationId, "groups", groupId, "feed"); + return q; + } + + /** + * Get a group's activity feed. Returned as a query to ease paging. Executes + * asynchronously in background and the callbacks are called in the UI + * thread. + * + * + * @param groupId + * @param callback + */ + public void queryActivityFeedForGroupAsync(final String groupId, + final QueryResultsCallback callback) { + (new ClientAsyncTask<Query>(callback) { + @Override + public Query doTask() { + return queryActivityFeedForGroup(groupId); + } + }).execute(); + } + + + /****************** ENTITY CONNECTIONS ***********************/ + /****************** ENTITY CONNECTIONS ***********************/ + + /** + * Connect two entities together. + * + * @param connectingEntityType The type of the first entity. + * @param connectingEntityId The ID of the first entity. + * @param connectionType The type of connection between the entities. + * @param connectedEntityId The ID of the second entity. + * @return An instance with the server's response. + */ + public ApiResponse connectEntities(String connectingEntityType, + String connectingEntityId, String connectionType, + String connectedEntityId) { + return apiRequest(HTTP_METHOD_POST, null, null, organizationId, applicationId, + connectingEntityType, connectingEntityId, connectionType, + connectedEntityId); + } + + /** + * Connect two entities together + * + * @param connectorType The type of the first entity in the connection. + * @param connectorID The first entity's ID. + * @param connectionType The type of connection to make. + * @param connecteeType The type of the second entity. + * @param connecteeID The second entity's ID + * @return An instance with the server's response. + */ + public ApiResponse connectEntities(String connectorType, + String connectorID, + String connectionType, + String connecteeType, + String connecteeID) { + return apiRequest(HTTP_METHOD_POST, null, null, organizationId, applicationId, + connectorType, connectorID, connectionType, connecteeType, connecteeID); + } + + + /** + * Connect two entities together. Executes asynchronously in background and + * the callbacks are called in the UI thread. + * + * @param connectingEntityType The type of the first entity. + * @param connectingEntityId The UUID or 'name' property of the first entity. + * @param connectionType The type of connection between the entities. + * @param connectedEntityId The UUID of the second entity. + * @param callback A callback with the async response. + */ + public void connectEntitiesAsync(final String connectingEntityType, + final String connectingEntityId, final String connectionType, + final String connectedEntityId, final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return connectEntities(connectingEntityType, + connectingEntityId, connectionType, connectedEntityId); + } + }).execute(); + } + + /** + * Connect two entities together. Allows the 'name' of the connected entity + * to be specified but requires the type also be specified. Executes asynchronously + * in background and the callbacks are called in the UI thread. + * + * @param connectingEntityType The type of the first entity. + * @param connectingEntityId The UUID or 'name' property of the first entity. + * @param connectionType The type of connection between the entities. + * @param connectedEntityType The type of connection between the entities. + * @param connectedEntityId The UUID or 'name' property of the second entity. + * @param callback A callback with the async response. + */ + public void connectEntitiesAsync(final String connectingEntityType, + final String connectingEntityId, final String connectionType, + final String connectedEntityType, final String connectedEntityId, + final ApiResponseCallback callback) { + (new ClientAsyncTask<ApiResponse>(callback) { + @Override + public ApiResponse doTask() { + return connectEntities(connectingEntityType, + connectingEntityId, connectionType, connectedEntityType, connectedEntityId); + } + }).execute(); + } + + /** + * Disconnect two entities. + * + * @param connectingEntityType The collection name or UUID of the first entity. + * @param connectingEntityId The name or UUID of the first entity. + * @param connectionType The type of connection between the entities. + * @param connectedEntityId The name or UUID of the second entity. + * @return An instance with the server's response. + */ + public ApiResponse disconnectEntities(String connectingEntityType, + String connectingEntityId, String connectionType, + String connectedEntity
<TRUNCATED>
