Skip to content

Commit

Permalink
Merge 28d5dfd into 7fdb02f
Browse files Browse the repository at this point in the history
  • Loading branch information
najmsheikh committed Jun 24, 2019
2 parents 7fdb02f + 28d5dfd commit 9f5c2d3
Show file tree
Hide file tree
Showing 7 changed files with 512 additions and 328 deletions.
213 changes: 42 additions & 171 deletions button-merchant/src/main/java/com/usebutton/merchant/ButtonApiImpl.java
Expand Up @@ -35,48 +35,29 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* Button API endpoint request implementations.
*/
final class ButtonApiImpl implements ButtonApi {

private static final String TAG = ButtonApiImpl.class.getSimpleName();

private static final int CONNECT_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(5);
private static final int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(15);
private static final String CONTENT_TYPE_JSON = "application/json";

private final String userAgent;

@VisibleForTesting
String baseUrl = "https://api.usebutton.com";

private static ButtonApi buttonApi;

static ButtonApi getInstance(String userAgent) {
private final ConnectionManager connectionManager;

static ButtonApi getInstance(ConnectionManager connectionManager) {
if (buttonApi == null) {
buttonApi = new ButtonApiImpl(userAgent);
buttonApi = new ButtonApiImpl(connectionManager);
}

return buttonApi;
}

@VisibleForTesting
ButtonApiImpl(String userAgent) {
this.userAgent = userAgent;
ButtonApiImpl(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}

@Nullable
Expand All @@ -85,173 +66,63 @@ static ButtonApi getInstance(String userAgent) {
public PostInstallLink getPendingLink(String applicationId, String ifa,
boolean limitAdTrackingEnabled, Map<String, String> signalsMap) throws
ButtonNetworkException {
HttpURLConnection urlConnection = null;

try {
// create request body
JSONObject requestBody = new JSONObject();
requestBody.put("application_id", applicationId);
requestBody.put("ifa", ifa);
requestBody.put("ifa_limited", limitAdTrackingEnabled);
requestBody.put("signals", new JSONObject(signalsMap));

// setup url connection
final URL url = new URL(baseUrl + "/v1/web/deferred-deeplink");
urlConnection = (HttpURLConnection) url.openConnection();
initializeUrlConnection(urlConnection);

// write request body
final OutputStreamWriter writer =
new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
writer.write(requestBody.toString());
writer.close();

Log.d(TAG, "Request Body: " + requestBody);
Log.d(TAG, "Response Code: " + urlConnection.getResponseCode());
// check if it's successful
if (urlConnection.getResponseCode() < 400) {
// read response body
final InputStream in = new BufferedInputStream(urlConnection.getInputStream());
final BufferedReader reader =
new BufferedReader(new InputStreamReader(in, "UTF-8"));
final StringBuilder responseString = new StringBuilder();
try {
String line;
while ((line = reader.readLine()) != null) {
responseString.append(line);
}
} finally {
reader.close();
// Create request body
JSONObject request = new JSONObject();
request.put("application_id", applicationId);
request.put("ifa", ifa);
request.put("ifa_limited", limitAdTrackingEnabled);
request.put("signals", new JSONObject(signalsMap));

// Execute POST request and parse response
NetworkResponse response = connectionManager.post("/v1/web/deferred-deeplink", request);
JSONObject responseBody = response.getBody().optJSONObject("object");
if (responseBody != null) {
boolean match = responseBody.getBoolean("match");
String id = responseBody.getString("id");
String action = responseBody.getString("action");
PostInstallLink.Attribution attribution = null;

JSONObject attributionJson = responseBody.optJSONObject("attribution");
if (attributionJson != null) {
String btnRef = attributionJson.getString("btn_ref");
String utmSource = attributionJson.optString("utm_source", null);
attribution = new PostInstallLink.Attribution(btnRef, utmSource);
}

// parse response body
JSONObject responseJson = new JSONObject(responseString.toString());
responseJson = responseJson.optJSONObject("object");
if (responseJson != null) {
boolean match = responseJson.getBoolean("match");
String id = responseJson.getString("id");
String action = responseJson.getString("action");
PostInstallLink.Attribution attribution = null;

JSONObject attributionJson = responseJson.optJSONObject("attribution");
if (attributionJson != null) {
String btnRef = attributionJson.getString("btn_ref");
String utmSource = attributionJson.optString("utm_source", null);
attribution = new PostInstallLink.Attribution(btnRef, utmSource);
}

return new PostInstallLink(match, id, action, attribution);
}
} else {
String message =
"Unsuccessful Request. HTTP StatusCode: " + urlConnection.getResponseCode();
Log.e(TAG, message);
throw new ButtonNetworkException(message);
return new PostInstallLink(match, id, action, attribution);
}
} catch (MalformedURLException e) {
Log.e(TAG, "MalformedURLException has occurred", e);
throw new ButtonNetworkException(e);
} catch (IOException e) {
Log.e(TAG, "IOException has occurred", e);
throw new ButtonNetworkException(e);
} catch (JSONException e) {
Log.e(TAG, "JSONException has occurred", e);
Log.e(TAG, "Error creating request body", e);
throw new ButtonNetworkException(e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}

return null;
}

private void initializeUrlConnection(HttpURLConnection urlConnection) throws ProtocolException {
urlConnection.setConnectTimeout(CONNECT_TIMEOUT);
urlConnection.setReadTimeout(READ_TIMEOUT);
urlConnection.setRequestProperty("User-Agent", getUserAgent());
urlConnection.setRequestProperty("Accept", CONTENT_TYPE_JSON);
urlConnection.setRequestProperty("Content-Type", CONTENT_TYPE_JSON);
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
}

@Override
public Void postActivity(String applicationId, String sourceToken, String timestamp,
Order order) throws ButtonNetworkException {
HttpURLConnection urlConnection = null;

try {
// create request body
JSONObject requestBody = new JSONObject();
requestBody.put("app_id", applicationId);
requestBody.put("user_local_time", timestamp);
requestBody.put("btn_ref", sourceToken);
requestBody.put("order_id", order.getId());
requestBody.put("total", order.getAmount());
requestBody.put("currency", order.getCurrencyCode());
requestBody.put("source", "merchant-library");

// setup url connection
final URL url = new URL(baseUrl + "/v1/activity/order");
urlConnection = (HttpURLConnection) url.openConnection();
initializeUrlConnection(urlConnection);

// write request body
final OutputStreamWriter writer =
new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
writer.write(requestBody.toString());
writer.close();

// check if it's successful
if (urlConnection.getResponseCode() < 400) {
// read response body
final InputStream in = new BufferedInputStream(urlConnection.getInputStream());
final BufferedReader reader =
new BufferedReader(new InputStreamReader(in, "UTF-8"));
final StringBuilder responseString = new StringBuilder();
try {
String line;
while ((line = reader.readLine()) != null) {
responseString.append(line);
}
} finally {
reader.close();
}

// parse response body
JSONObject responseJson = new JSONObject(responseString.toString());

String status = responseJson.optJSONObject("meta").optString("status");
if (status.equals("error")) {
String error = responseJson.optJSONObject("error").optString("message");
throw new ButtonNetworkException(error);
}
} else {
String message =
"Unsuccessful Request. HTTP StatusCode: " + urlConnection.getResponseCode();
Log.e(TAG, message);
throw new ButtonNetworkException(message);
}
} catch (MalformedURLException e) {
Log.e(TAG, "MalformedURLException has occurred", e);
throw new ButtonNetworkException(e);
} catch (IOException e) {
Log.e(TAG, "IOException has occurred", e);
throw new ButtonNetworkException(e);
// Create request body
JSONObject request = new JSONObject();
request.put("app_id", applicationId);
request.put("user_local_time", timestamp);
request.put("btn_ref", sourceToken);
request.put("order_id", order.getId());
request.put("total", order.getAmount());
request.put("currency", order.getCurrencyCode());
request.put("source", "merchant-library");

// Execute POST request and parse response
connectionManager.post("/v1/activity/order", request);
} catch (JSONException e) {
Log.e(TAG, "JSONException has occurred", e);
Log.e(TAG, "Error creating request body", e);
throw new ButtonNetworkException(e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}

return null;
}

private String getUserAgent() {
return userAgent;
}
}
Expand Up @@ -49,6 +49,7 @@ private ButtonMerchant() {
static ButtonInternal buttonInternal = new ButtonInternalImpl(executor);

private static ExecutorService executorService = Executors.newSingleThreadExecutor();
private static final String BASE_URL = "https://api.usebutton.com";

/**
* Configures {@link ButtonMerchant} with your application Id.
Expand Down Expand Up @@ -163,7 +164,10 @@ private static ButtonRepository getButtonRepository(Context context) {

DeviceManager deviceManager = getDeviceManager(context);

ButtonApi buttonApi = ButtonApiImpl.getInstance(deviceManager.getUserAgent());
ConnectionManager connectionManager =
ConnectionManagerImpl.getInstance(BASE_URL, deviceManager.getUserAgent());

ButtonApi buttonApi = ButtonApiImpl.getInstance(connectionManager);

return ButtonRepositoryImpl.getInstance(buttonApi, persistenceManager, executorService);
}
Expand Down
@@ -0,0 +1,41 @@
/*
* ConnectionManager.java
*
* Copyright (c) 2019 Button, Inc. (https://usebutton.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package com.usebutton.merchant;

import android.support.annotation.Nullable;

import com.usebutton.merchant.exception.ButtonNetworkException;

import org.json.JSONObject;

/**
* Internal interface to facilitate common HTTP requests.
*/
interface ConnectionManager {

NetworkResponse post(String url, @Nullable JSONObject body)
throws ButtonNetworkException;
}

0 comments on commit 9f5c2d3

Please sign in to comment.