Skip to content

Instantly share code, notes, and snippets.

@virattt
Last active July 1, 2020 21:18
Show Gist options
  • Save virattt/ed527240ee08baa57b10b79410ff43bc to your computer and use it in GitHub Desktop.
Save virattt/ed527240ee08baa57b10b79410ff43bc to your computer and use it in GitHub Desktop.
package com.example.libjava;
import com.example.libjava.models.Response;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AirtableApi {
/**
* Requirements:
* 1. To make the API call, given a 'key'
* 2. Has mechanism (shouldMakeBatchAPICall()), which is dependent on time (time of first request vs. time of current request)
* 3. createBatchAPICall(...)
* 4. return appropriate response to the correct caller
*
* Order of operations:
* - Prevent rapid network calls (if client tries to do this, then batch all requests)
* -
*
* Thoughts:
* - keys:
* => Only add keys for a given batch
* => Once batch request is successfully executed, then clear the keys
*
*/
private final long WINDOW_MS = 1000;
private long previousBatchRequestMs = 0;
private Set<String> batch;
private Map<String, List<Listener>> listenerMap;
public AirtableApi() {
}
public void executeAPICall(String key, Listener listener) {
if (shouldCreateNewBatch(System.currentTimeMillis())) {
// Create new batch
batch = createBatch(key);
listenerMap = createListenerMap(key, listener);
} else {
// Add to existing batch
addToBatch(key);
addToListenerMap(key, listener);
}
makeAPICall(batch, listener);
}
private void makeAPICall(Set<String> batch, listener) {
Observable<Response> networkRequest = createNetworkRequest(batch);
networkRequest
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(new Consumer<Response>() {
@Override
public void accept(Response response) throws Throwable {
// Return this response to callers
for (String key : listenerMap.keys()) {
List<Listener> listeners = listenerMap.get(key);
for (Listener listener : listeners) {
listener.onResponse(response.getResponseForKey(key));
}
}
}
});
}
private Observable<Response> createNetworkRequest(final Set<String> batch) {
return Observable.create(new ObservableOnSubscribe<Response>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Response> emitter) throws Throwable {
try {
// Make API call
Response response = apiCall(batch);
emitter.onNext(response);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
});
}
private Response apiCall(Set<String> batch) {
return new Response();
}
private boolean shouldCreateNewBatch(long currentTimeMillis) {
if (currentTimeMillis - previousBatchRequestMs > WINDOW_MS) {
previousBatchRequestMs = currentTimeMillis;
return true;
}
return false;
}
private Set<String> createBatch(String key) {
batch = new HashSet<>();
batch.add(key);
return batch;
}
private Map<String, List<Listener>> createListenerMap(String key, Listener listener) {
listenerMap = new HashMap<String, List<Listener>>();
listenerMap.put(key, new ArrayList<>(listener));
return listenerMap;
}
private String addToBatch(String key) {
batch.add(key);
return key;
}
private String addToBatch(String key, Listener listener) {
List<Listener> listeners = listenerMap.getOrDefault(key, new ArrayList<>());
listeners.add(listener);
listenerMap.put(key, listeners)
return key;
}
}
package com.example.libjava.models;
public class Response {
public Response() {
}
/**
* {
* "foo": 12,
* "bar": 42
* }
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment