public interface UberAuthService {

    public static final String SCOPE_PROFILE = "profile";
    public static final String SCOPE_HISTORY_LITE = "history_lite";
    public static final String SCOPE_REQUEST = "request";

    public static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
    public static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token";

    /**
     * Exchange this authorization code for an access_token, which will allow you to make requests on
     * behalf of a user. The access_token expires in 30 days. The grant_type may be authorization_code or refresh_token.
     *
     * @param clientId A 32 character string (public)
     * @param clientSecret A 40 character string. DO NOT SHARE. This should not be available on any public facing
     *                     server or web site. If you have been compromised or shared this token accidentally please
     *                     reset your client_secret via our web interface. This will require updating your application
     *                     to use the newly issued client_secret.
     * @param redirectUri
     * @param grantType
     * @param authorizationCode
     * @param refreshToken
     * @return AccessToken
     */
    @POST("/oauth/token")
    AccessToken requestAccessToken(@Query("client_id") String clientId,
                                   @Query("client_secret") String clientSecret,
                                   @Query("redirect_uri") String redirectUri,
                                   @Query("grant_type") String grantType,
                                   @Query("code") String authorizationCode,
                                   @Query("refresh_token") String refreshToken);

    /**
     * @see #requestAccessToken(String, String, String, String, String, String)
     */
    @POST("/oauth/token")
    void requestAccessToken(@Query("client_id") String clientId,
                            @Query("client_secret") String clientSecret,
                            @Query("redirect_uri") String redirectUri,
                            @Query("grant_type") String grantType,
                            @Query("code") String authorizationCode,
                            @Query("refresh_token") String refreshToken,
                            Callback<AccessToken> callback);



} public class Utils {

    //*************
    // Error helpers
    //*************

    /**
     * Parses surge confirmation retrofit error (409) into a model object
     * @param gson
     * @param retrofitError
     * @return
     * @throws IOException
     */
    public static SurgeConfirmationError parseSurgeConfirmationError(Gson gson, RetrofitError retrofitError) throws IOException {
        String body = IOUtils.toString(retrofitError.getResponse().getBody().in(), "UTF-8");
        IOUtils.closeQuietly(retrofitError.getResponse().getBody().in());
        return gson.fromJson(body, SurgeConfirmationError.class);
    }

    //*************
    // OAuth helpers
    //*************

    /**
     * Generates the initial url for the OAuth authorization flow
     * @return oauth authorize url
     */
    public static String generateAuthorizeUrl(String oAuthUri, String clientId, String[] scope){
        StringBuilder sb = new StringBuilder(oAuthUri);
        sb.append("/oauth/authorize?response_type=code");
        sb.append("&client_id=" + clientId);
        if (scope != null) {
            sb.append("&scope=");
            for (int i = 0; i < scope.length; i++) {
                if (i > 0) {
                    sb.append("%20");
                }
                sb.append(scope[i]);
            }
        }
        return sb.toString();
    }
} public class AccessToken {

    @Expose
    @SerializedName("access_token")
    private String accessToken;

    @Expose
    @SerializedName("refresh_token")
    private String refreshToken;

    @Expose
    @SerializedName("token_type")
    private String tokenType;

    @Expose
    @SerializedName("expires_in")
    private String expiresIn;

    @Expose
    @SerializedName("scope")
    private String scope;

    public AccessToken() {
    }

    public String getAccessToken() {
        return this.accessToken;
    }

    public String getTokenType() {
        return this.tokenType;
    }

    public void setAccessToken(String paramString) {
        this.accessToken = paramString;
    }

    public void setTokenType(String paramString) {
        this.tokenType = paramString;
    }

    public String getExpiresIn() {
        return expiresIn;
    }

    public void setExpiresIn(String expiresIn) {
        this.expiresIn = expiresIn;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getRefreshToken() {
        return refreshToken;
    }

    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
} public class Products {

    @Expose
    @SerializedName("products")
    private List<Product> products;

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }
} public class UserActivity {

    @Expose
    @SerializedName("offset")
    private int offset;
    @Expose
    @SerializedName("limit")
    private int limit;
    @Expose
    @SerializedName("count")
    private int count;
    @Expose
    @SerializedName("history")
    private List<UserHistory> history;

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public List<UserHistory> getHistory() {
        return history;
    }

    public void setHistory(List<UserHistory> history) {
        this.history = history;
    }
} public class Request {

    /**
     * The unique ID of the Request.
     */
    @Expose
    @SerializedName("request_id")
    private String requestId;

    /**
     * The status of the Request indicating state.
     */
    @Expose
    @SerializedName("status")
    private RequestBody.Status status;

    /**
     * The object that contains vehicle details.
     */
    @Expose
    @SerializedName("vehicle")
    private Vehicle vehicle;

    /**
     * The object that contains driver details.
     */
    @Expose
    @SerializedName("driver")
    private Driver driver;

    /**
     * The object that contains the location information of the vehicle and driver.
     */
    @Expose
    @SerializedName("location")
    private Location location;

    /**
     * The estimated time of vehicle arrival in minutes.
     */
    @Expose
    @SerializedName("eta")
    private int eta;

    /**
     * The surge pricing multiplier used to calculate the increased price of a Request. A
     * multiplier of 1.0 means surge pricing is not in effect.
     */
    @Expose
    @SerializedName("surge_multiplier")
    private float surgeMultiplier;

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public RequestBody.Status getStatus() {
        return status;
    }

    public void setStatus(RequestBody.Status status) {
        this.status = status;
    }

    public Vehicle getVehicle() {
        return vehicle;
    }

    public void setVehicle(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver(Driver driver) {
        this.driver = driver;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    public int getEta() {
        return eta;
    }

    public void setEta(int eta) {
        this.eta = eta;
    }

    public float getSurgeMultiplier() {
        return surgeMultiplier;
    }

    public void setSurgeMultiplier(float surgeMultiplier) {
        this.surgeMultiplier = surgeMultiplier;
    }
} public class RequestBody {

    public RequestBody(String productId, double startLatitude, double startLongitude,
                       double endLatitude, double endLongitude, String surgeConfirmationId) {
        this.productId = productId;
        this.startLatitude = startLatitude;
        this.startLongitude = startLongitude;
        this.endLatitude = endLatitude;
        this.endLongitude = endLongitude;
        this.surgeConfirmationId = surgeConfirmationId;
    }

    public enum Status {
        /**
         * The Request is matching to the most efficient available driver.
         */
        @SerializedName("processing")
        PROCESSING("processing"),
        /**
         * The Request has been accepted by a driver and is "en route" to the start_location.
         */
        @SerializedName("accepted")
        ACCEPTED("accepted"),
        /**
         * The driver has arrived or will be shortly.
         */
        @SerializedName("arriving")
        ARRIVING("arriving"),
        /**
         * The Request is "en route" from the start location to the end location.
         */
        @SerializedName("in_progress")
        IN_PROGRESS("in_progress"),
        /**
         * The Request has been canceled by the driver.
         */
        @SerializedName("driver_canceled")
        DRIVER_CANCELLED("driver_canceled"),
        /**
         * Request has been completed by the driver.
         */
        @SerializedName("completed")
        COMPLETED("completed"),
        /**
         * The Request was unfulfilled because no drivers were available. Use the Product Types sandbox endpoint to
         * create a Request with this status.
         */
        @SerializedName("no_drivers_available")
        NO_DRIVERS_AVAILABLE("no_drivers_available"),
        /**
         * The Request canceled by rider. Issue a DELETE command to give a Request this status
         */
        @SerializedName("rider_canceled")
        RIDER_CANCELED("rider_canceled");

        private final String status;
        Status(String status) {
            this.status = status;
        }
        public String value() {
            return status;
        }
    }

    @Expose
    @SerializedName("product_id")
    private String productId;

    @Expose
    @SerializedName("start_latitude")
    private double startLatitude;

    @Expose
    @SerializedName("start_longitude")
    private double startLongitude;

    @Expose
    @SerializedName("end_latitude")
    private double endLatitude;

    @Expose
    @SerializedName("end_longitude")
    private double endLongitude;

    @Expose
    @SerializedName("surge_confirmation_id")
    private String surgeConfirmationId;


    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public double getStartLatitude() {
        return startLatitude;
    }

    public void setStartLatitude(double startLatitude) {
        this.startLatitude = startLatitude;
    }

    public double getStartLongitude() {
        return startLongitude;
    }

    public void setStartLongitude(double startLongitude) {
        this.startLongitude = startLongitude;
    }

    public double getEndLatitude() {
        return endLatitude;
    }

    public void setEndLatitude(double endLatitude) {
        this.endLatitude = endLatitude;
    }

    public double getEndLongitude() {
        return endLongitude;
    }

    public void setEndLongitude(double endLongitude) {
        this.endLongitude = endLongitude;
    }

    public String getSurgeConfirmationId() {
        return surgeConfirmationId;
    }

    public void setSurgeConfirmationId(String surgeConfirmationId) {
        this.surgeConfirmationId = surgeConfirmationId;
    }
} public class RequestMap {
    @Expose
    @SerializedName("request_id")
    private String requestId;

    @Expose
    @SerializedName("href")
    private String href;

    public String getRequestId() {
        return requestId;
    }

    public void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    public String getHref() {
        return href;
    }

    public void setHref(String href) {
        this.href = href;
    }
} public class SurgeConfirmationError {

    @Expose
    @SerializedName("meta")
    private Meta meta;
    @Expose
    @SerializedName("errors")
    private List<Error> errors;

    public Meta getMeta() {
        return meta;
    }

    public void setMeta(Meta meta) {
        this.meta = meta;
    }

    public List<Error> getErrors() {
        return errors;
    }

    public void setErrors(List<Error> errors) {
        this.errors = errors;
    }
} public class SandboxProductBody {

    public SandboxProductBody(Float surgeMultiplier, Boolean driversAvailable) {
        this.surgeMultiplier = surgeMultiplier;
        this.driversAvailable = driversAvailable;
    }

    @Expose
    @SerializedName("surge_multiplier")
    private Float surgeMultiplier;
    @Expose
    @SerializedName("drivers_available")
    private Boolean driversAvailable;

    public float getSurgeMultiplier() {
        return surgeMultiplier;
    }

    public void setSurgeMultiplier(float surgeMultiplier) {
        this.surgeMultiplier = surgeMultiplier;
    }

    public boolean isDriversAvailable() {
        return driversAvailable;
    }

    public void setDriversAvailable(boolean driversAvailable) {
        this.driversAvailable = driversAvailable;
    }
} public class SandboxRequestBody {

    @Expose
    @SerializedName("status")
    private RequestBody.Status status;

    public SandboxRequestBody(RequestBody.Status status) {
        this.status = status;
    }

    public RequestBody.Status getStatus() {
        return status;
    }

    public void setStatus(RequestBody.Status status) {
        this.status = status;
    }
}
import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
import com.gargoylesoftware.htmlunit.util.WebConnectionWrapper;
import com.squareup.okhttp.Response;
import com.victorsima.uber.UberAuthService;
import com.victorsima.uber.Utils;
import com.victorsima.uber.model.AccessToken;
import com.victorsima.uber.model.Products;
import com.victorsima.uber.model.UserActivity;
import com.victorsima.uber.model.request.Request;
import com.victorsima.uber.model.request.RequestBody;
import com.victorsima.uber.model.request.RequestMap;
import com.victorsima.uber.model.request.SurgeConfirmationError;
import com.victorsima.uber.model.sandbox.SandboxProductBody;
import com.victorsima.uber.model.sandbox.SandboxRequestBody;
import org.junit.Before;
import org.junit.Test;
import retrofit.RetrofitError;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collections;
import static org.junit.Assert.*;
package com.victorsima.uber.test;




/**
 * Tests for requests using uber sandbox
 *
 */
@SuppressWarnings("unchecked")
public class SandboxServerTest extends BaseTest {

    private static final int delay = 3000;// use a delay to allow the PUT calls to take effect

    @Override
    public boolean runUsingSandboxServer() {
        return true;
    }

    @Override
    @Before
    public void setup() throws Exception {
        super.setup();
        if (!client.hasAccessToken()) {
            retrieveAccessToken();
        }
    }

    @Test
    public void testAccessTokenRefresh() {
        String oldAccessToken = client.getAccessToken();
        AccessToken refreshedAccessToken = client.getAuthService().requestAccessToken(clientId, clientSecret,
                client.getClientRedirectUri(), UberAuthService.GRANT_TYPE_REFRESH_TOKEN, null, client.getRefreshToken());
        assertNotNull(refreshedAccessToken);
        assertNotEquals("The access token was not refreshed", oldAccessToken, refreshedAccessToken.getAccessToken());
    }

    @Test
    public void testHistory() throws Exception {
        UserActivity userActivity = client.getApiService().getUserActivity(null, null);
        assertNotNull("User Activity is null", userActivity);
        assertNotNull(userActivity.getHistory());
        assertTrue(userActivity.getHistory().size() > 0);
    }

    @Test
    public void testRiderCancelRequestStatus() throws Exception {
        Request request = getRequest();
        client.getApiService().deleteRequest(request.getRequestId());
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.RIDER_CANCELED);
    }

    @Test
    public void testNoDriverAvailableRequestStatus() throws Exception {
        Products products = client.getApiService().getProducts(Double.parseDouble(latitude), Double.parseDouble(longitude));
        String productId = products.getProducts().get(0).getProductId();
        client.getSandboxService().putProducts(productId, new SandboxProductBody(null, false));
        Thread.sleep(delay);
        try {
            Request request = client.getApiService().postRequest(new RequestBody(productId,
                    Double.parseDouble(startLatitude),
                    Double.parseDouble(startLongitude),
                    Double.parseDouble(endLatitude),
                    Double.parseDouble(endLongitude),
                    null));
        } catch (RetrofitError re) {
            assertEquals(re.getResponse().getStatus(), 409);
            assertEquals(re.getResponse().getReason(), RequestBody.Status.NO_DRIVERS_AVAILABLE.value());
        }

    }

    @Test
    public void testRequestStatus() throws Exception {
        Request request = getRequest();
        Thread.sleep(delay);
        /**
         * processing is the default state and cannot be updated, simply check status
         */
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.PROCESSING);

        client.getSandboxService().putRequest(request.getRequestId(), new SandboxRequestBody(RequestBody.Status.ACCEPTED));
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.ACCEPTED);

        client.getSandboxService().putRequest(request.getRequestId(), new SandboxRequestBody(RequestBody.Status.ARRIVING));
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.ARRIVING);

        client.getSandboxService().putRequest(request.getRequestId(), new SandboxRequestBody(RequestBody.Status.IN_PROGRESS));
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.IN_PROGRESS);

        client.getSandboxService().putRequest(request.getRequestId(), new SandboxRequestBody(RequestBody.Status.DRIVER_CANCELLED));
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.DRIVER_CANCELLED);
    }

    @Test
    public void testCompletedRequestStatus() throws Exception {
        Request request = getRequest();
        Response response = client.getSandboxService().putRequest(request.getRequestId(), new SandboxRequestBody(RequestBody.Status.COMPLETED));
        Thread.sleep(delay);
        request = client.getApiService().getRequestDetails(request.getRequestId());
        assertEquals(request.getStatus(), RequestBody.Status.COMPLETED);
    }

    @Test
    public void testRequestMap() throws Exception {
        Request request = getRequest();
        RequestMap requestMap = client.getApiService().getRequestMap(request.getRequestId());