Merge pull request #1920 from Shyri/android-volley-library

Android volley library enhancement and tests
This commit is contained in:
wing328 2016-01-22 10:52:26 +08:00
commit 03b463bc43
6 changed files with 598 additions and 30 deletions

View File

@ -17,9 +17,12 @@ import com.android.volley.VolleyError;
import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
{{#operations}}
public class {{classname}} {
@ -44,11 +47,93 @@ public class {{classname}} {
{{#operation}}
/**
* {{summary}}
* {{notes}}
* {{summary}}
* {{notes}}
{{#allParams}} * @param {{paramName}} {{description}}
{{/allParams}} * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
*/
*/
public {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws TimeoutException, ExecutionException, InterruptedException, ApiException {
Object postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
{{#allParams}}{{#required}}
// verify the required parameter '{{paramName}}' is set
if ({{paramName}} == null) {
VolleyError error = new VolleyError("Missing the required parameter '{{paramName}}' when calling {{nickname}}",
new ApiException(400, "Missing the required parameter '{{paramName}}' when calling {{nickname}}"));
}
{{/required}}{{/allParams}}
// create path and map variables
String path = "{{path}}".replaceAll("\\{format\\}","json"){{#pathParams}}.replaceAll("\\{" + "{{baseName}}" + "\\}", apiInvoker.escapeString({{{paramName}}}.toString())){{/pathParams}};
// query params
List<Pair> queryParams = new ArrayList<Pair>();
// header params
Map<String, String> headerParams = new HashMap<String, String>();
// form params
Map<String, String> formParams = new HashMap<String, String>();
{{#queryParams}}
queryParams.addAll(ApiInvoker.parameterToPairs("{{#collectionFormat}}{{{collectionFormat}}}{{/collectionFormat}}", "{{baseName}}", {{paramName}}));
{{/queryParams}}
{{#headerParams}}
headerParams.put("{{baseName}}", ApiInvoker.parameterToString({{paramName}}));
{{/headerParams}}
String[] contentTypes = {
{{#consumes}}"{{mediaType}}"{{#hasMore}},{{/hasMore}}{{/consumes}}
};
String contentType = contentTypes.length > 0 ? contentTypes[0] : "application/json";
if (contentType.startsWith("multipart/form-data")) {
// file uploading
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
{{#formParams}}{{#notFile}}
if ({{paramName}} != null) {
builder.addTextBody("{{baseName}}", ApiInvoker.parameterToString({{paramName}}), ApiInvoker.TEXT_PLAIN_UTF8);
}
{{/notFile}}{{#isFile}}
if ({{paramName}} != null) {
builder.addBinaryBody("{{baseName}}", {{paramName}});
}
{{/isFile}}{{/formParams}}
HttpEntity httpEntity = builder.build();
postBody = httpEntity;
} else {
// normal form params
{{#formParams}}{{#notFile}}formParams.put("{{baseName}}", ApiInvoker.parameterToString({{paramName}}));{{/notFile}}
{{/formParams}}
}
String[] authNames = new String[] { {{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}} };
try {
String response = apiInvoker.invokeAPI (basePath, path, "{{httpMethod}}", queryParams, postBody, headerParams, formParams, contentType, authNames);
if(response != null){
return {{#returnType}}({{{returnType}}}) ApiInvoker.deserialize(response, "{{returnContainer}}", {{returnBaseType}}.class){{/returnType}};
} else {
return {{#returnType}}null{{/returnType}};
}
} catch (ApiException ex) {
throw ex;
} catch (InterruptedException ex) {
throw ex;
} catch (ExecutionException ex) {
if(ex.getCause() instanceof VolleyError) {
throw new ApiException(((VolleyError) ex.getCause()).networkResponse.statusCode, ((VolleyError) ex.getCause()).getMessage());
}
throw ex;
} catch (TimeoutException ex) {
throw ex;
}
}
/**
* {{summary}}
* {{notes}}
{{#allParams}} * @param {{paramName}} {{description}}{{/allParams}}
*/
public void {{nickname}} ({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}, {{/hasMore}}{{/allParams}}final Response.Listener<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}String{{/returnType}}> responseListener, final Response.ErrorListener errorListener) {
Object postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};

View File

@ -1,10 +1,16 @@
package {{invokerPackage}};
import android.content.Context;
import com.android.volley.Cache;
import com.android.volley.Network;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.toolbox.Volley;
import com.android.volley.ResponseDelivery;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.HttpStack;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.NoCache;
import com.android.volley.toolbox.RequestFuture;
import com.google.gson.JsonParseException;
import org.apache.http.Consts;
@ -22,6 +28,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import {{invokerPackage}}.auth.Authentication;
import {{invokerPackage}}.auth.ApiKeyAuth;
@ -36,11 +45,12 @@ public class ApiInvoker {
private static ApiInvoker INSTANCE;
private Map<String, String> defaultHeaderMap = new HashMap<String, String>();
private Context context;
private RequestQueue mRequestQueue;
private Map<String, Authentication> authentications;
private int connectionTimeout;
/** Content type "text/plain" with UTF-8 encoding. */
public static final ContentType TEXT_PLAIN_UTF8 = ContentType.create("text/plain", Consts.UTF_8);
@ -165,8 +175,16 @@ public class ApiInvoker {
return params;
}
public static void initializeInstance(Context context) {
INSTANCE = new ApiInvoker(context);
public static void initializeInstance() {
initializeInstance(null);
}
public static void initializeInstance(Cache cache) {
initializeInstance(cache, null, 0, null, 30);
}
public static void initializeInstance(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery, int connectionTimeout) {
INSTANCE = new ApiInvoker(cache, network, threadPoolSize, delivery, connectionTimeout);
setUserAgent("Android-Volley-Swagger");
// Setup authentications (key: authentication name, value: authentication).
@ -178,17 +196,27 @@ public class ApiInvoker {
{{#isBasic}}
INSTANCE.authentications.put("{{name}}", new HttpBasicAuth());
{{/isBasic}}
{{#isOAuth}}
INSTANCE.authentications.put("{{name}}", new OAuth());
{{/isOAuth}}
{{/authMethods}}
// Prevent the authentications from being modified.
INSTANCE.authentications = Collections.unmodifiableMap(INSTANCE.authentications);
}
private ApiInvoker(Context context) {
this.context = context;
initConnectionManager();
}
public ApiInvoker() {
initConnectionManager();
private ApiInvoker(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery, int connectionTimeout) {
if(cache == null) cache = new NoCache();
if(network == null) {
HttpStack stack = new HurlStack();
network = new BasicNetwork(stack);
}
if(delivery == null) {
initConnectionRequest(cache, network);
} else {
initConnectionRequest(cache, network, threadPoolSize, delivery);
}
this.connectionTimeout = connectionTimeout;
}
public static ApiInvoker getInstance() {
@ -304,6 +332,14 @@ public class ApiInvoker {
throw new RuntimeException("No API key authentication configured!");
}
public void setConnectionTimeout(int connectionTimeout){
this.connectionTimeout = connectionTimeout;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
/**
* Update query and header parameters based on authentication settings.
*
@ -317,7 +353,21 @@ public class ApiInvoker {
}
}
public String invokeAPI(String host, String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String contentType, String[] authNames) throws ApiException, InterruptedException, ExecutionException, TimeoutException {
RequestFuture<String> future = RequestFuture.newFuture();
Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, future, future);
if(request != null) {
mRequestQueue.add(request);
return future.get(connectionTimeout, TimeUnit.SECONDS);
} else return "no data";
}
public void invokeAPI(String host, String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String contentType, String[] authNames, Response.Listener<String> stringRequest, Response.ErrorListener errorListener) throws ApiException {
Request request = createRequest(host, path, method, queryParams, body, headerParams, formParams, contentType, authNames, stringRequest, errorListener);
if (request != null) mRequestQueue.add(request);
}
public Request<String> createRequest(String host, String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> formParams, String contentType, String[] authNames, Response.Listener<String> stringRequest, Response.ErrorListener errorListener) throws ApiException {
StringBuilder b = new StringBuilder();
b.append("?");
@ -374,13 +424,13 @@ public class ApiInvoker {
}
formParamStr = formParamBuilder.toString();
}
Request request = null;
if ("GET".equals(method)) {
GetRequest request = new GetRequest(url, headers, null, stringRequest, errorListener);
mRequestQueue.add(request);
request = new GetRequest(url, headers, null, stringRequest, errorListener);
}
else if ("POST".equals(method)) {
PostRequest request = null;
request = null;
if (formParamStr != null) {
request = new PostRequest(url, headers, contentType, new StringEntity(formParamStr, "UTF-8"), stringRequest, errorListener);
} else if (body != null) {
@ -389,11 +439,12 @@ public class ApiInvoker {
} else {
request = new PostRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);
}
} else {
request = new PostRequest(url, headers, null, null, stringRequest, errorListener);
}
if(request != null) mRequestQueue.add(request);
}
else if ("PUT".equals(method)) {
PutRequest request = null;
request = null;
if (formParamStr != null) {
request = new PutRequest(url, headers, contentType, new StringEntity(formParamStr, "UTF-8"), stringRequest, errorListener);
} else if (body != null) {
@ -402,11 +453,12 @@ public class ApiInvoker {
} else {
request = new PutRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);
}
} else {
request = new PutRequest(url, headers, null, null, stringRequest, errorListener);
}
if(request != null) mRequestQueue.add(request);
}
else if ("DELETE".equals(method)) {
DeleteRequest request = null;
request = null;
if (formParamStr != null) {
request = new DeleteRequest(url, headers, contentType, new StringEntity(formParamStr, "UTF-8"), stringRequest, errorListener);
} else if (body != null) {
@ -415,11 +467,12 @@ public class ApiInvoker {
} else {
request = new DeleteRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);
}
} else {
request = new DeleteRequest(url, headers, null, null, stringRequest, errorListener);
}
if(request != null) mRequestQueue.add(request);
}
else if ("PATCH".equals(method)) {
PatchRequest request = null;
request = null;
if (formParamStr != null) {
request = new PatchRequest(url, headers, contentType, new StringEntity(formParamStr, "UTF-8"), stringRequest, errorListener);
} else if (body != null) {
@ -428,12 +481,24 @@ public class ApiInvoker {
} else {
request = new PatchRequest(url, headers, contentType, new StringEntity(serialize(body), "UTF-8"), stringRequest, errorListener);
}
}
if(request != null) mRequestQueue.add(request);
} else {
request = new PatchRequest(url, headers, null, null, stringRequest, errorListener);
}
}
return request;
}
private void initConnectionManager() {
mRequestQueue = Volley.newRequestQueue(context);
private void initConnectionRequest(Cache cache, Network network) {
mRequestQueue = new RequestQueue(cache, network);
mRequestQueue.start();
}
private void initConnectionRequest(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
mRequestQueue = new RequestQueue(cache, network, threadPoolSize, delivery);
mRequestQueue.start();
}
public void stopQueue() {
mRequestQueue.stop();
}
}

View File

@ -44,6 +44,9 @@ public class ApiKeyAuth implements Authentication {
@Override
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) {
String value;
if (apiKey == null) {
return;
}
if (apiKeyPrefix != null) {
value = apiKeyPrefix + " " + apiKey;
} else {

View File

@ -0,0 +1,13 @@
package {{invokerPackage}}.auth;
import {{invokerPackage}}.Pair;
import java.util.Map;
import java.util.List;
public class OAuth implements Authentication {
@Override
public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) {
// TODO stub
}
}

View File

@ -49,6 +49,10 @@ android {
}
}
}
testOptions {
unitTests.returnDefaultValues = true
}
}
@ -58,6 +62,8 @@ ext {
httpclient_version = "4.3.3"
volley_version = "1.0.19"
junit_version = "4.8.1"
robolectric_version = "3.0"
concurrent_unit_version = "0.4.2"
}
dependencies {
@ -67,6 +73,8 @@ dependencies {
compile "org.apache.httpcomponents:httpmime:$httpclient_version"
compile "com.mcxiaoke.volley:library:${volley_version}@aar"
testCompile "junit:junit:$junit_version"
testCompile "org.robolectric:robolectric:${robolectric_version}"
testCompile "net.jodah:concurrentunit:${concurrentunitVersion}"
}
afterEvaluate {

View File

@ -0,0 +1,394 @@
package es.shyri.swagger.android.volley.petstore.full;
import com.android.volley.ExecutorDelivery;
import com.android.volley.Network;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.HttpStack;
import com.android.volley.toolbox.HurlStack;
import com.android.volley.toolbox.NoCache;
import net.jodah.concurrentunit.Waiter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import io.swagger.client.ApiException;
import io.swagger.client.ApiInvoker;
import io.swagger.client.api.PetApi;
import io.swagger.client.model.Category;
import io.swagger.client.model.Pet;
import static com.ibm.icu.impl.Assert.fail;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(RobolectricTestRunner.class)
public class PetApiTest {
PetApi api = null;
@Before
public void setup() {
HttpStack stack = new HurlStack();
Network network = new BasicNetwork(stack);
ApiInvoker.initializeInstance(new NoCache(), network, 4, new ExecutorDelivery(Executors.newSingleThreadExecutor()), 30);
api = new PetApi();
}
@Test
public void testCreateAndGetPet() throws Exception {
final Waiter waiter = new Waiter();
final Pet pet = createRandomPet();
api.addPet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.getPetById(pet.getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet response) {
Pet fetched = response;
waiter.assertNotNull(fetched);
waiter.assertEquals(pet.getId(), fetched.getId());
waiter.assertNotNull(fetched.getCategory());
waiter.assertEquals(fetched.getCategory().getName(), pet.getCategory().getName());
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
}
@Test
public void testUpdatePet() throws Exception {
final Waiter waiter = new Waiter();
final Pet pet = createRandomPet();
pet.setName("programmer");
api.updatePet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.getPetById(pet.getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet fetched) {
waiter.assertNotNull(fetched);
waiter.assertEquals(pet.getId(), fetched.getId());
waiter.assertNotNull(fetched.getCategory());
waiter.assertEquals(fetched.getCategory().getName(), pet.getCategory().getName());
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
}
@Test
public void testFindPetsByStatus() throws Exception {
final Waiter waiter = new Waiter();
final Pet pet = createRandomPet();
pet.setName("programmer");
pet.setStatus(Pet.StatusEnum.available);
api.updatePet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.findPetsByStatus(Arrays.asList(new String[]{"available"}), new Response.Listener<List<Pet>>() {
@Override
public void onResponse(List<Pet> pets) {
waiter.assertNotNull(pets);
boolean found = false;
for (Pet fetched : pets) {
if (fetched.getId().equals(pet.getId())) {
found = true;
break;
}
}
waiter.assertTrue(found);
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
}
@Test
public void testUpdatePetWithForm() throws Exception {
final Waiter waiter = new Waiter();
final Pet pet = createRandomPet();
pet.setName("frank");
api.addPet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
final Pet[] fetched = new Pet[1];
api.getPetById(pet.getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet petResponse) {
fetched[0] = petResponse;
waiter.assertEquals("frank", fetched[0].getName());
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.updatePetWithForm(String.valueOf(fetched[0].getId()), "furt", null, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.getPetById(fetched[0].getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet updated) {
waiter.assertEquals("furt", updated.getName());
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
}
@Test
public void testDeletePet() throws Exception {
final Waiter waiter = new Waiter();
Pet pet = createRandomPet();
api.addPet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
final Pet[] fetched = new Pet[1];
api.getPetById(pet.getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet response) {
fetched[0] = response;
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.deletePet(fetched[0].getId(), "special-key", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
api.getPetById(fetched[0].getId(), new Response.Listener<Pet>() {
@Override
public void onResponse(Pet response) {
waiter.fail("expected an error");
waiter.resume();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
waiter.assertEquals(404, error.networkResponse.statusCode);
waiter.resume();
}
});
waiter.await();
}
@Test
public void testUploadFile() throws Exception {
final Waiter waiter = new Waiter();
Pet pet = createRandomPet();
api.addPet(pet, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
File file = new File("hello.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write("Hello world!");
writer.close();
api.uploadFile(pet.getId(), "a test file", new File(file.getAbsolutePath()), new Response.Listener<String>() {
@Override
public void onResponse(String response) {
waiter.resume();
}
}, createErrorListener(waiter));
waiter.await();
}
@Test
public void testCreateAndGetPetSync() throws Exception {
Pet pet = createRandomPet();
api.addPet(pet);
Pet fetched = api.getPetById(pet.getId());
assertNotNull(fetched);
assertEquals(pet.getId(), fetched.getId());
assertNotNull(fetched.getCategory());
assertEquals(fetched.getCategory().getName(), pet.getCategory().getName());
}
@Test
public void testUpdatePetSync() throws Exception {
Pet pet = createRandomPet();
pet.setName("programmer");
api.updatePet(pet);
Pet fetched = api.getPetById(pet.getId());
assertNotNull(fetched);
assertEquals(pet.getId(), fetched.getId());
assertNotNull(fetched.getCategory());
assertEquals(fetched.getCategory().getName(), pet.getCategory().getName());
}
@Test
public void testFindPetsByStatusSync() throws Exception {
Pet pet = createRandomPet();
pet.setName("programmer");
pet.setStatus(Pet.StatusEnum.available);
api.updatePet(pet);
List<Pet> pets = api.findPetsByStatus(Arrays.asList(new String[]{"available"}));
assertNotNull(pets);
boolean found = false;
for (Pet fetched : pets) {
if (fetched.getId().equals(pet.getId())) {
found = true;
break;
}
}
assertTrue(found);
}
@Test
public void testUpdatePetWithFormSync() throws Exception {
Pet pet = createRandomPet();
pet.setName("frank");
api.addPet(pet);
Pet fetched = api.getPetById(pet.getId());
assertEquals("frank", fetched.getName());
api.updatePetWithForm(String.valueOf(fetched.getId()), "furt", null);
Pet updated = api.getPetById(fetched.getId());
assertEquals("furt", updated.getName());
}
@Test
public void testDeletePetSync() throws Exception {
Pet pet = createRandomPet();
api.addPet(pet);
Pet fetched = api.getPetById(pet.getId());
api.deletePet(fetched.getId(), null);
try {
fetched = api.getPetById(fetched.getId());
fail("expected an error");
} catch (ApiException e) {
assertEquals(404, e.getCode());
}
}
@Test
public void testUploadFileSync() throws Exception {
Pet pet = createRandomPet();
api.addPet(pet);
File file = new File("hello.txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write("Hello world!");
writer.close();
api.uploadFile(pet.getId(), "a test file", new File(file.getAbsolutePath()));
}
private Pet createRandomPet() {
Pet pet = new Pet();
pet.setId(System.currentTimeMillis());
pet.setName("gorilla");
Category category = new Category();
category.setName("really-happy");
pet.setCategory(category);
pet.setStatus(Pet.StatusEnum.available);
List<String> photos = Arrays.asList(new String[]{"http://foo.bar.com/1", "http://foo.bar.com/2"});
pet.setPhotoUrls(photos);
return pet;
}
private Response.ErrorListener createErrorListener(final Waiter waiter) {
return new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
waiter.fail(error.getMessage());
waiter.resume();
}
};
}
}