Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bda2ad5a authored by David Luhmer's avatar David Luhmer
Browse files

add all supported operations / add unit tests

parent 4c92f072
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -52,4 +52,9 @@ dependencies {

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.okhttp3:okhttp:3.8.0'

    // Required for local unit tests (JUnit 4 framework)
    testImplementation 'junit:junit:4.12'
    // required if you want to use Mockito for unit tests
    testImplementation 'org.mockito:mockito-core:2.23.4'
}
+55 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@

package com.nextcloud.android.sso.aidl;

import android.support.annotation.VisibleForTesting;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
@@ -40,7 +42,7 @@ public class NextcloudRequest implements Serializable {

    private NextcloudRequest() { }

    public static class Builder {
    public static class Builder implements Cloneable {
        private NextcloudRequest ncr;

        public Builder() {
@@ -95,6 +97,10 @@ public class NextcloudRequest implements Serializable {
            ncr.followRedirects = followRedirects;
            return this;
        }

        public Object clone() throws CloneNotSupportedException{
            return super.clone();
        }
    }

    public String getMethod() {
@@ -144,4 +150,52 @@ public class NextcloudRequest implements Serializable {
    public boolean isFollowRedirects() {
        return this.followRedirects;
    }


    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }

        if (!(obj instanceof NextcloudRequest)) {
            return false;
        }


        NextcloudRequest rq = (NextcloudRequest)obj;
        boolean equal;
        equal  = checkEqual("accountName", this.accountName, rq.accountName);
        equal &= checkEqual("header", this.header, rq.header);
        equal &= checkEqual("method", this.method, rq.method);
        equal &= checkEqual("packageName", this.packageName, rq.packageName);
        equal &= checkEqual("parameter", this.parameter, rq.parameter);
        equal &= checkEqual("requestBody", this.requestBody, rq.requestBody);
        equal &= checkEqual("token", this.token, rq.token);
        equal &= checkEqual("url", this.url, rq.url);
        equal &= checkEqual("followRedirects", this.followRedirects, rq.followRedirects);

        return equal;

        //return super.equals(obj);
    }

    private boolean checkEqual(String name, Object o1, Object o2) {
        if(o1 == null && o2 == null) {
            return true;
        }

        if(o1 != null) {
            boolean eq = o1.equals(o2);
            if(!eq) {
                System.err.println("[" + name + " ] Expected: " + o1 + " Was: " + o2);
            }
            return eq;
        } else {
            // o1 == null and o2 != null
            System.err.println("[" + name + " ] Expected: " + o1 + " Was: " + o2);
        }

        return false;
    }
}
+24 −2
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@ import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicBoolean;

@@ -57,6 +60,8 @@ import io.reactivex.Observable;
import io.reactivex.annotations.NonNull;

import static com.nextcloud.android.sso.exceptions.SSOException.parseNextcloudCustomException;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

public class NextcloudAPI {

@@ -70,7 +75,12 @@ public class NextcloudAPI {
    private ApiConnectedListener mCallback;
    private Context mContext;

    @Documented
    @Target(METHOD)
    @Retention(RUNTIME)
    public @interface FollowRedirects{

    }

    public interface ApiConnectedListener {
        void onConnected();
@@ -83,8 +93,15 @@ public class NextcloudAPI {
        this.gson = gson;
        this.mCallback = callback;

        new Thread()
        {
            public void run() {
                Log.d(TAG, "run() called " + Thread.currentThread().getName());
                connectApiWithBackoff();
            }
        }.start();
        //connectApiWithBackoff();
    }

    private String getAccountName() {
        return mAccount.name;
@@ -104,6 +121,7 @@ public class NextcloudAPI {
    }

    private void connect() {
        Log.v(TAG, "Nextcloud Single sign-on connect() called [" + Thread.currentThread().getName() + "]");
        if (mDestroyed) {
            throw new IllegalStateException("API already destroyed! You cannot reuse a stopped API instance");
        }
@@ -152,7 +170,7 @@ public class NextcloudAPI {
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.i(TAG, "Nextcloud Single sign-on: onServiceConnected");
            Log.v(TAG, "Nextcloud Single sign-on: onServiceConnected [" + Thread.currentThread().getName() + "]");

            mService = IInputStreamService.Stub.asInterface(service);
            mBound.set(true);
@@ -315,4 +333,8 @@ public class NextcloudAPI {
        ois.close();
        return result;
    }

    protected Gson getGson() {
        return gson;
    }
}
+55 −40
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package com.nextcloud.android.sso.api;
import android.support.annotation.Nullable;
import android.util.Log;

import com.google.gson.GsonBuilder;
import com.nextcloud.android.sso.aidl.NextcloudRequest;
import com.nextcloud.android.sso.helper.Okhttp3Helper;
import com.nextcloud.android.sso.helper.Retrofit2Helper;
@@ -13,8 +14,10 @@ import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@@ -25,17 +28,21 @@ import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HEAD;
import retrofit2.http.HTTP;
import retrofit2.http.Header;
import retrofit2.http.Multipart;
import retrofit2.http.OPTIONS;
import retrofit2.http.PATCH;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.Streaming;

public class NextcloudRetrofitServiceMethod<T> {

@@ -50,69 +57,55 @@ public class NextcloudRetrofitServiceMethod<T> {
    private static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
    private static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);


    private Method method;
    String httpMethod;
    private String httpMethod;
    private @Nullable String relativeUrl;
    private @Nullable Headers headers;
    private @Nullable MediaType contentType;
    private boolean hasBody;
    private boolean isFormEncoded;
    private boolean isMultipart;
    private NextcloudAPI nextcloudAPI;
    private Set<String> relativeUrlParamNames;
    private Type returnType;
    private boolean followRedirects = false;

    NextcloudRequest.Builder nextcloudRequest;
    private final NextcloudRequest.Builder requestBuilder;

    private final String mApiEndpoint;
    private Set<String> relativeUrlParamNames;


    public NextcloudRetrofitServiceMethod(String apiEndpoint, Method method) {
        this.method = method;
        this.returnType = method.getGenericReturnType();

        /*
        if(this.returnType instanceof ParameterizedType){
            ParameterizedType type = (ParameterizedType) returnType;
            Type[] typeArguments = type.getActualTypeArguments();
            for(Type typeArgument : typeArguments){
                Log.d(TAG, "NextcloudRetrofitServiceMethod() " + typeArgument);
            }
        }
        */


        this.methodAnnotations = method.getAnnotations();
        this.parameterTypes = method.getGenericParameterTypes();
        this.parameterAnnotationsArray = method.getParameterAnnotations();
        this.mApiEndpoint = apiEndpoint;

        for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
        }

        this.mApiEndpoint = apiEndpoint;



        if(headers == null) {
            headers = new Headers.Builder().build();
        }

        nextcloudRequest = new NextcloudRequest.Builder()
        requestBuilder = new NextcloudRequest.Builder()
                .setMethod(httpMethod)
                .setHeader(headers.toMultimap())
                .setFollowRedirects(followRedirects)
                .setUrl(new File(this.mApiEndpoint,relativeUrl).toString());

        if(headers != null) {
            nextcloudRequest.setHeader(headers.toMultimap());
        }




        Log.d(TAG, "NextcloudRetrofitServiceMethod() called with: apiEndpoint = [" + apiEndpoint + "], method = [" + method + "]");

    }

    public T invoke(NextcloudAPI nextcloudAPI, Object[] args) throws Exception {

        Map<String, String> parameters = new HashMap<>();

        NextcloudRequest.Builder rBuilder = (NextcloudRequest.Builder) requestBuilder.clone();

        if(parameterAnnotationsArray.length != args.length) {
            throw new InvalidParameterException("Expected: " + parameterAnnotationsArray.length + " params - were: " + args.length);
        }
@@ -122,15 +115,28 @@ public class NextcloudRetrofitServiceMethod<T> {

            if(annotation instanceof Query) {
                parameters.put(((Query)annotation).value(), args[i].toString());
            } else if(annotation instanceof Body) {
                rBuilder.setRequestBody(nextcloudAPI.getGson().toJson(args[i]));
            } else if(annotation instanceof Path) {
                String varName = "{" + ((Path)annotation).value() + "}";
                String url = rBuilder.build().getUrl();
                rBuilder.setUrl(url.replace(varName, args[i].toString()));
            } else if(annotation instanceof Header) {
                Map<String, List<String>> headers = rBuilder.build().getHeader();
                List<String> arg = new ArrayList<>();
                arg.add(args[i].toString());
                headers.put(((Header)annotation).value(), arg);
                rBuilder.setHeader(headers);
            } else {
                throw new UnsupportedOperationException("don't know this type yet.. [" + annotation.toString() + "]");
            }
        }

        NextcloudRequest request = nextcloudRequest
        NextcloudRequest request = rBuilder
                .setParameter(parameters)
                .build();


        if(this.returnType instanceof ParameterizedType){
            ParameterizedType type = (ParameterizedType) returnType;
            Type ownerType = type.getRawType();
@@ -151,7 +157,6 @@ public class NextcloudRetrofitServiceMethod<T> {
        }

        return nextcloudAPI.performRequest(this.returnType, request);

    }

    
@@ -163,25 +168,34 @@ public class NextcloudRetrofitServiceMethod<T> {
            parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
        } else if (annotation instanceof GET) {
            parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
        } else if (annotation instanceof HEAD) {
            parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
        } else if (annotation instanceof PATCH) {
            parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
        } else if (annotation instanceof POST) {
            parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
        } else if (annotation instanceof PUT) {
            parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
        } else if (annotation instanceof OPTIONS) {
            parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
        } else if (annotation instanceof HTTP) {
            HTTP http = (HTTP) annotation;
            parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
        } else if (annotation instanceof Streaming) {
            Log.v(TAG, "streaming interface");
        } else if (annotation instanceof retrofit2.http.Headers) {
            String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
            if (headersToParse.length == 0) {
                throw methodError(method, "@Headers annotation is empty.");
            }
            headers = parseHeaders(headersToParse);
        } else if(annotation instanceof NextcloudAPI.FollowRedirects) {
            followRedirects = true;
        } else {
            throw new UnsupportedOperationException(annotation.toString());
        }

        /*
        else if (annotation instanceof HEAD) {
            parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
        } else if (annotation instanceof PATCH) {
            parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
        } else if (annotation instanceof OPTIONS) {
            parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
        } else if (annotation instanceof HTTP) {
            HTTP http = (HTTP) annotation;
            parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
        } else if (annotation instanceof Multipart) {
            if (isFormEncoded) {
                throw methodError(method, "Only one encoding annotation is allowed.");
@@ -193,6 +207,7 @@ public class NextcloudRetrofitServiceMethod<T> {
            }
            isFormEncoded = true;
        }
        */
    }

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
+31 −0
Original line number Diff line number Diff line
package android.util;

// https://stackoverflow.com/questions/36787449/how-to-mock-method-e-in-log
public class Log {
    public static int d(String tag, String msg) {
        System.out.println("DEBUG: " + tag + ": " + msg);
        return 0;
    }

    public static int i(String tag, String msg) {
        System.out.println("INFO: " + tag + ": " + msg);
        return 0;
    }

    public static int w(String tag, String msg) {
        System.out.println("WARN: " + tag + ": " + msg);
        return 0;
    }

    public static int v(String tag, String msg) {
        System.out.println("VERBOSE: " + tag + ": " + msg);
        return 0;
    }

    public static int e(String tag, String msg) {
        System.out.println("ERROR: " + tag + ": " + msg);
        return 0;
    }

    // add other methods if required...
}
Loading