Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bac 1531 spike for adding data to http headers on mobile side #549

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -267,5 +267,7 @@ class TransferSummaryIntegrationTest {
override fun getSource(): Document.Source = Document.Source.newSource("androidTest")

override fun isReviewable(): Boolean = false

override fun generateUploadMetadata(context: Context?): String = ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -262,5 +262,7 @@ class TransferSummaryIntegrationTest {
override fun getSource(): Document.Source = Document.Source.newSource("androidTest")

override fun isReviewable(): Boolean = false

override fun generateUploadMetadata(context: Context?): String = ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.gini.android.capture.network

import android.content.Context
import android.text.TextUtils
import androidx.annotation.VisibleForTesting
import androidx.annotation.XmlRes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -57,10 +58,14 @@ import net.gini.android.bank.api.models.Configuration as BankConfiguration
* [GiniCapture.Builder.setGiniCaptureNetworkService] when creating a
* [GiniCapture] instance.
*/
class GiniCaptureDefaultNetworkService(
class GiniCaptureDefaultNetworkService

@VisibleForTesting
internal constructor(
internal val giniBankApi: GiniBankAPI,
private val documentMetadata: DocumentMetadata?,
coroutineContext: CoroutineContext = Dispatchers.Main
private val context: Context,
coroutineContext: CoroutineContext = Dispatchers.Main,
) : GiniCaptureNetworkService {

private val coroutineScope = CoroutineScope(coroutineContext)
Expand Down Expand Up @@ -212,12 +217,18 @@ class GiniCaptureDefaultNetworkService(
return@launchCancellable
}

val uploadMetadata = document.generateUploadMetadata(context)

val partialDocumentResource = giniBankApi.documentManager.createPartialDocument(
document = documentData,
contentType = document.mimeType,
filename = null,
documentType = null,
documentMetadata
documentMetadata?.copy()?.apply {
setUploadMetadata(uploadMetadata)
} ?: DocumentMetadata().apply {
setUploadMetadata(uploadMetadata)
}
)

when (partialDocumentResource) {
Expand Down Expand Up @@ -512,7 +523,7 @@ class GiniCaptureDefaultNetworkService(
trustManager?.let { giniApiBuilder.setTrustManager(it) }
giniApiBuilder.setDebuggingEnabled(isDebuggingEnabled)
val giniBankApi = giniApiBuilder.build()
return GiniCaptureDefaultNetworkService(giniBankApi, documentMetadata)
return GiniCaptureDefaultNetworkService(giniBankApi, documentMetadata, mContext)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.gini.android.capture.network

import android.net.Uri
import android.os.Looper
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import io.mockk.*
Expand All @@ -13,7 +14,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Shadows.shadowOf
import java.util.*
import kotlin.collections.LinkedHashMap

/**
* Created by Alpár Szotyori on 25.02.22.
Expand Down Expand Up @@ -56,7 +56,7 @@ class GiniCaptureDefaultNetworkServiceTest {

// Mock DocumentTaskManager returning the mock documents
val documentManager = mockk<BankApiDocumentManager>()
coEvery { documentManager.createPartialDocument(any(), any(), null, null) } returns Resource.Success(partialDocument)
coEvery { documentManager.createPartialDocument(any(), any(), null, null, any()) } returns Resource.Success(partialDocument)
coEvery { documentManager.createCompositeDocument(any<LinkedHashMap<net.gini.android.core.api.models.Document, Int>>(), any()) } returns Resource.Success(compositeDocument)
coEvery { documentManager.pollDocument(any()) } returns Resource.Success(compositeDocument)
coEvery { documentManager.getAllExtractionsWithPolling(any()) } returns Resource.Success(mockk())
Expand All @@ -65,13 +65,14 @@ class GiniCaptureDefaultNetworkServiceTest {
val bankApi = mockk<GiniBankAPI>()
every { bankApi.documentManager } returns documentManager

val networkService = GiniCaptureDefaultNetworkService(bankApi, null)
val networkService = GiniCaptureDefaultNetworkService(bankApi, null, ApplicationProvider.getApplicationContext())

// Mock Gini Capture SDK document
val captureDocument = mockk<Document>()
every { captureDocument.id } returns "id"
every { captureDocument.data } returns byteArrayOf()
every { captureDocument.mimeType } returns "image/jpeg"
every { captureDocument.generateUploadMetadata(ApplicationProvider.getApplicationContext()) } returns ""

// When
networkService.upload(captureDocument, mockk(relaxed = true))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package net.gini.android.capture;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.HashMap;
import java.util.Map;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;


/**
* This class is the container for transferring documents between the client application and the
Expand Down Expand Up @@ -120,6 +121,12 @@ public interface Document extends Parcelable {
*/
boolean isReviewable();

/**
* Generate metadata to be sent to backend when creating partial document.
*
*/
String generateUploadMetadata(Context context);

/**
* Supported document types.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
import android.net.Uri;
import android.os.Parcel;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import net.gini.android.capture.AsyncCallback;
import net.gini.android.capture.Document;
import net.gini.android.capture.internal.camera.photo.ParcelableMemoryCache;
import net.gini.android.capture.internal.util.DeviceHelper;
import net.gini.android.capture.internal.util.UriReaderAsyncTask;
import net.gini.android.capture.util.IntentHelper;

import java.util.Arrays;
import java.util.UUID;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
* Internal use only.
*
Expand Down Expand Up @@ -219,6 +220,16 @@ public String getParcelableMemoryCacheTag() {
return mParcelableMemoryCacheTag;
}

@Override
public String generateUploadMetadata(Context context) {
return UploadMetadata.INSTANCE
.setSource(mSource.getName())
.setDeviceType(DeviceHelper.getDeviceType(context))
.setDeviceOrientation(DeviceHelper.getDeviceOrientation(context))
.setImportMethod(mImportMethod.name())
.build();
}

@Override
public String toString() {
return "GiniCaptureDocument{"
Expand Down Expand Up @@ -309,6 +320,7 @@ public boolean equals(final Object o) {
if (mImportMethod != that.mImportMethod) {
return false;
}

return mMimeType.equals(that.mMimeType);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package net.gini.android.capture.document

import android.os.Build
import net.gini.android.capture.BuildConfig
import net.gini.android.capture.EntryPoint
import net.gini.android.capture.GiniCapture

internal object UploadMetadata {

private const val USER_COMMENT_PLATFORM = "Platform"
private const val USER_COMMENT_OS_VERSION = "OSVer"
private const val USER_COMMENT_GINI_CAPTURE_VERSION = "GiniVisionVer"
private const val USER_COMMENT_DEVICE_ORIENTATION = "DeviceOrientation"
private const val USER_COMMENT_DEVICE_TYPE = "DeviceType"
private const val USER_COMMENT_SOURCE = "Source"
private const val USER_COMMENT_IMPORT_METHOD = "ImportMethod"
private const val USER_COMMENT_ENTRY_POINT = "EntryPoint"

private var giniCaptureVersion: String = ""
private var deviceOrientation: String = ""
private var deviceType: String = ""
private var source: String = ""
private var importMethod: String = ""

private fun convertMapToCSV(keyValueMap: Map<String, String>): String {
val csvBuilder = StringBuilder()
var isFirst = true
for ((key, value) in keyValueMap) {
if (!isFirst) {
csvBuilder.append(',')
}
isFirst = false
csvBuilder.append(key)
.append('=')
.append(value)
}
return csvBuilder.toString()
}

fun setDeviceOrientation(deviceOrientation: String): UploadMetadata =
this.also { it.deviceOrientation = deviceOrientation }

fun setDeviceType(deviceType: String): UploadMetadata =
this.also { it.deviceType = deviceType }

fun setSource(source: String): UploadMetadata = this.also { it.source = source }

fun setImportMethod(importMethod: String): UploadMetadata = this.also { it.importMethod = importMethod }

fun build(): String {
val metadataMap = mutableMapOf<String, String>()

metadataMap[USER_COMMENT_PLATFORM] = "Android"
metadataMap[USER_COMMENT_OS_VERSION] = Build.VERSION.RELEASE.toString()
if (giniCaptureVersion.isNotEmpty()) {
metadataMap[USER_COMMENT_GINI_CAPTURE_VERSION] = giniCaptureVersion
}
if (deviceOrientation.isNotEmpty()) {
metadataMap[USER_COMMENT_DEVICE_ORIENTATION] = deviceOrientation
}
if (deviceType.isNotEmpty()) {
metadataMap[USER_COMMENT_DEVICE_TYPE] = deviceType
}
if (source.isNotEmpty()) {
metadataMap[USER_COMMENT_SOURCE] = source
}
if (importMethod.isNotEmpty()) {
metadataMap[USER_COMMENT_IMPORT_METHOD] = importMethod
}
metadataMap[USER_COMMENT_GINI_CAPTURE_VERSION] = BuildConfig.VERSION_NAME.replace(" ", "")

if (GiniCapture.hasInstance()) {
metadataMap[USER_COMMENT_ENTRY_POINT] = entryPointToString(GiniCapture.getInstance().getEntryPoint())
} else {
metadataMap[USER_COMMENT_ENTRY_POINT] = entryPointToString(GiniCapture.Internal.DEFAULT_ENTRY_POINT)
}
return convertMapToCSV(metadataMap)
}

private fun entryPointToString(entryPoint: EntryPoint) = when (entryPoint) {
EntryPoint.FIELD -> "field"
EntryPoint.BUTTON -> "button"
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package net.gini.android.core.api;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.HashMap;
import java.util.Map;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

/**
* Created by Alpar Szotyori on 25.10.2018.
*
Expand All @@ -26,6 +26,8 @@ public class DocumentMetadata {
public static final String HEADER_FIELD_NAME_PREFIX = "X-Document-Metadata-";
@VisibleForTesting
public static final String BRANCH_ID_HEADER_FIELD_NAME = HEADER_FIELD_NAME_PREFIX + "BranchId";
@VisibleForTesting
public static final String UPLOAD_METADATA_HEADER_FIELD_NAME = HEADER_FIELD_NAME_PREFIX + "Upload";


private final Map<String, String> mMetadataMap = new HashMap<>();
Expand Down Expand Up @@ -63,6 +65,19 @@ public void setBranchId(@NonNull final String branchId) throws IllegalArgumentEx
}
}

/**
* Set upload metadata to be sent to backend
*
* @param uploadMetadata containing info related to the device, file import type etc...
*/
public void setUploadMetadata(@NonNull final String uploadMetadata) {
if (isASCIIEncodable(uploadMetadata)) {
mMetadataMap.put(UPLOAD_METADATA_HEADER_FIELD_NAME, uploadMetadata);
} else {
throw new IllegalArgumentException("Metadata is not encodable as ASCII: " + uploadMetadata);
}
}

@VisibleForTesting
public boolean isASCIIEncodable(@NonNull final String string) {
if (mAsciiCharsetEncoder != null) {
Expand Down Expand Up @@ -98,6 +113,17 @@ public void add(@NonNull final String name, @NonNull final String value)
mMetadataMap.put(completeName, value);
}

/**
* Provides a copy of the [DocumentMetadata] object
*
* @return the copy of the metadata object
*/
public DocumentMetadata copy() {
DocumentMetadata copy = new DocumentMetadata();
mMetadataMap.forEach((key, value) -> copy.add(key, value));
return copy;
}

@NonNull
@VisibleForTesting
public Map<String, String> getMetadata() {
Expand Down
Loading