/**
   * Builds a request to connect a user to Facebook.
   *
   * @param facebookAccessToken the Facebook access token to use to connect the user.
   * @return {@link AbstractRequest} to use to connect the user to Facebook.
   */
  @NonNull
  @LevelUpApi(contract = Contract.INTERNAL)
  @AccessTokenRequired
  public AbstractRequest buildFacebookConnectRequest(@NonNull String facebookAccessToken) {
    PreconditionUtil.assertNotNull(facebookAccessToken, "facebookAccessToken");

    JSONObject object = new JSONObject();
    JSONObject userObject = new JSONObject();

    try {
      userObject.put(PARAM_FACEBOOK_ACCESS_TOKEN, facebookAccessToken);
      object.put(OUTER_PARAM_USER, userObject);
    } catch (JSONException e) {
      LogManager.e("JSONException building register request", e);
    }

    return new LevelUpRequest(
        getContext(),
        HttpMethod.POST,
        LevelUpRequest.API_VERSION_CODE_V14,
        FACEBOOK_CONNECTION_ENDPOINT,
        null,
        new JSONObjectRequestBody(object),
        getAccessTokenRetriever());
  }
 /**
  * Sets a pending request parameter, allowing {@link org.json.JSONObject}.NULL values.
  *
  * @param key The key of the parameter.
  * @param value The value of the parameter.
  */
 private void setParam(@NonNull String key, @Nullable String value) {
   try {
     mParams.put(key, value == null ? JSONObject.NULL : value);
   } catch (JSONException e) {
     LogManager.e("JSONException when adding key(%s) to body", key, e);
   }
 }
    /**
     * Adds the User's custom attributes. An empty or null custom attribute value removes the custom
     * attribute's key from the pending update request. The request is not modified if the custom
     * attribute set is null.
     *
     * @param key The custom attribute key.
     * @param value The custom attribute value.
     * @return The {@link UserInfoRequestBuilder} for chaining convenience.
     */
    @NonNull
    public UserInfoRequestBuilder withCustomAttribute(@NonNull String key, @Nullable String value) {
      try {
        mCustomAttributes.put(key, value);
      } catch (JSONException e) {
        LogManager.e("JSONException when adding custom attribute", e);
      }

      return this;
    }
    /**
     * Helper method to get the full JSON object to post.
     *
     * @return the full {@link JSONObject} to post to the server.
     */
    @NonNull
    private JSONObject getParams() {
      if (0 < mCustomAttributes.length()) {

        try {
          mParams.put(PARAM_CUSTOM_ATTRIBUTES, mCustomAttributes);
        } catch (JSONException e) {
          LogManager.e("JSONException when adding custom attributes to body", e);
        }
      }

      // Nest the parameters in the outer 'user' field.
      JSONObject requestParameters = new JSONObject();

      try {
        requestParameters.put(OUTER_PARAM_USER, mParams);
      } catch (JSONException e) {
        LogManager.e("JSONException when building the user update request", e);
      }

      LogManager.v("Building user update request with parameters %s", requestParameters);

      return requestParameters;
    }
  /**
   * Build a request to create a new user. Will also get the device UUID and append it to the
   * request.
   *
   * @param firstName user's first name.
   * @param lastName user's last name.
   * @param email user's email address.
   * @param password user's new password.
   * @param location Android Location for the user (or null if none was found).
   * @return {@link AbstractRequest} representing the register request.
   */
  @NonNull
  @LevelUpApi(contract = Contract.ENTERPRISE)
  public AbstractRequest buildRegisterRequest(
      @NonNull String firstName,
      @NonNull String lastName,
      @NonNull String email,
      @NonNull String password,
      @Nullable Location location) {
    PreconditionUtil.assertNotNull(firstName, "firstName");
    PreconditionUtil.assertNotNull(lastName, "lastName");
    PreconditionUtil.assertNotNull(email, "email");
    PreconditionUtil.assertNotNull(password, "password");

    JSONObject object = new JSONObject();
    JSONObject userObject = new JSONObject();

    try {
      Context context = getContext();

      RequestUtils.addApiKeyToRequestBody(context, object);
      userObject.put(PARAM_FIRST_NAME, firstName);
      userObject.put(PARAM_LAST_NAME, lastName);
      userObject.put(PARAM_EMAIL, email);
      userObject.put(PARAM_TERMS_ACCEPTED, true);
      userObject.put(PARAM_PASSWORD, password);
      RequestUtils.addDeviceIdToRequestBody(context, userObject);

      if (null != location) {
        userObject.put(PARAM_LATITUDE, location.getLatitude());
        userObject.put(PARAM_LONGITUDE, location.getLongitude());
      }

      object.put(OUTER_PARAM_USER, userObject);
    } catch (JSONException e) {
      LogManager.e("JSONException building register request", e);
    }

    return new LevelUpRequest(
        getContext(),
        HttpMethod.POST,
        LevelUpRequest.API_VERSION_CODE_V14,
        USERS_ENDPOINT,
        null,
        new JSONObjectRequestBody(object));
  }
  /**
   * Build a request to create a new user.
   *
   * @param firstName user's first name.
   * @param lastName user's last name.
   * @param email user's email address.
   * @param permissions array of permissions to grant the new user
   * @return {@link AbstractRequest} representing the register request.
   */
  @NonNull
  @LevelUpApi(contract = Contract.PUBLIC)
  public AbstractRequest buildRegisterRequest(
      @NonNull String firstName,
      @NonNull String lastName,
      @NonNull String email,
      @NonNull List<String> permissions) {
    PreconditionUtil.assertNotNull(firstName, "firstName");
    PreconditionUtil.assertNotNull(lastName, "lastName");
    PreconditionUtil.assertNotNull(email, "email");
    PreconditionUtil.assertNotNull(permissions, "permissions");

    JSONObject object = new JSONObject();
    JSONObject userObject = new JSONObject();
    JSONArray permissionArray = new JSONArray();

    try {
      RequestUtils.addApiKeyToRequestBody(getContext(), object);
      userObject.put(PARAM_FIRST_NAME, firstName);
      userObject.put(PARAM_LAST_NAME, lastName);
      userObject.put(PARAM_EMAIL, email);

      for (String permission : permissions) {
        permissionArray.put(permission);
      }

      object.put(OUTER_PARAM_USER, userObject);
      object.put(OUTER_PARAM_PERMISSION_KEYNAMES, permissionArray);
    } catch (JSONException e) {
      LogManager.e("JSONException building register request", e);
    }

    return new LevelUpRequest(
        getContext(),
        HttpMethod.POST,
        LevelUpRequest.API_VERSION_CODE_V15,
        APPS_USERS_ENDPOINT,
        null,
        new JSONObjectRequestBody(object));
  }
  /**
   * Build a request to register a new User via a facebook access token.
   *
   * @param facebookAccessToken the facebook access token to use to register the new user.
   * @return the {@link AbstractRequest} to use to register the new user.
   */
  @NonNull
  @LevelUpApi(contract = Contract.INTERNAL)
  public AbstractRequest buildFacebookRegisterRequest(@NonNull String facebookAccessToken) {
    JSONObject object = new JSONObject();
    JSONObject userObject = new JSONObject();

    try {
      RequestUtils.addApiKeyToRequestBody(getContext(), object);
      userObject.put(PARAM_FACEBOOK_ACCESS_TOKEN, facebookAccessToken);

      object.put(OUTER_PARAM_USER, userObject);
    } catch (JSONException e) {
      LogManager.e("JSONException building facebook register request", e);
    }

    return new LevelUpRequest(
        getContext(),
        HttpMethod.POST,
        LevelUpRequest.API_VERSION_CODE_V14,
        USERS_ENDPOINT,
        null,
        new JSONObjectRequestBody(object));
  }