diff --git a/app/app-dependencies.gradle b/app/app-dependencies.gradle index 45ba4a62..68cd5a83 100644 --- a/app/app-dependencies.gradle +++ b/app/app-dependencies.gradle @@ -19,17 +19,17 @@ ext { // the version number in one location for those artifacts demoAppSharedVersions = [ okhttp3Version : '3.2.0', - supportLibraryVersion : '25.0.1', + supportLibraryVersion : '25.2.0', leakCanaryVersion : '1.5', bvSdkVersion : VERSION_NAME, stethoVersion : '1.3.1', butterknifeVersion : '8.5.0', daggerVersion: '2.8', - playServicesVersion: '9.8.0' + playServicesVersion: '11.0.0' ] demoAppBuildDep = [ - androidPlugin : 'com.android.tools.build:gradle:2.3.2', + androidPlugin : 'com.android.tools.build:gradle:2.3.3', crashlyticsPlugin : 'io.fabric.tools:gradle:1.21.6', ] @@ -62,6 +62,7 @@ ext { processPhoenix : "com.jakewharton:process-phoenix:2.0.0", bvSdkAnalytics : "com.bazaarvoice.bvandroidsdk:analytics:$demoAppSharedVersions.bvSdkVersion", bvSdkAds : "com.bazaarvoice.bvandroidsdk:advertising:$demoAppSharedVersions.bvSdkVersion", + bvSdkAuthIovation : "com.bazaarvoice.bvandroidsdk:auth-iovation:$demoAppSharedVersions.bvSdkVersion", bvSdkConversations : "com.bazaarvoice.bvandroidsdk:conversations:$demoAppSharedVersions.bvSdkVersion", bvSdkCurations : "com.bazaarvoice.bvandroidsdk:curations:$demoAppSharedVersions.bvSdkVersion", bvSdkRecommendations: "com.bazaarvoice.bvandroidsdk:recommendations:$demoAppSharedVersions.bvSdkVersion", diff --git a/app/build.gradle b/app/build.gradle index 2bfcfb6c..fb10d40a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,11 +4,6 @@ buildscript { classpath demoAppBuildDep.crashlyticsPlugin // classpath demoAppBuildDep.androidAptPlugin } - repositories { - flatDir { - dirs 'libs' - } - } } repositories { @@ -103,6 +98,7 @@ dependencies { if (useLocalSdk) { compile project(':bvanalytics') compile project(':bvadvertising') + compile project(':bvauthiovation') compile project(':bvrecommendations') compile project(':bvconversations') compile project(':bvcurations') @@ -114,6 +110,8 @@ dependencies { } else { compile demoAppDep.bvSdkAnalytics compile demoAppDep.bvSdkAds + compile demoAppDep.bvSdkAuthIovation + compile('com.iovation.deviceprint.lib.DevicePrint:deviceprint-lib-release-2.3.3:2.3.3@aar') compile demoAppDep.bvSdkRecommendations compile demoAppDep.bvSdkConversations compile demoAppDep.bvSdkCurations @@ -145,14 +143,6 @@ dependencies { compile demoAppDep.butterknife compile demoAppDep.processPhoenix annotationProcessor demoAppDep.butterknifeCompiler - // iovation aar dependency - // Uncomment the following lines to add iovation support - // Make sure the iovation arr is in the app/libs folder - // dependencies { - // compile fileTree(dir: 'libs', include: ['*.jar']) - // compile(name:'deviceprint-release-2.3.0', ext:'aar') - // compile(group:'com.android.support', name:'support-v4', version:'23.0.1') - // } releaseCompile demoAppDep.leakCanaryNoOp debugCompile demoAppDep.leakCanary testCompile demoAppTestDep.junit diff --git a/app/libs/deviceprint-lib-release-2.3.3.aar b/app/libs/deviceprint-lib-release-2.3.3.aar new file mode 100644 index 00000000..227a0939 Binary files /dev/null and b/app/libs/deviceprint-lib-release-2.3.3.aar differ diff --git a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java index 6a0e7d49..c30fa6d6 100644 --- a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java +++ b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/DemoBvModule.java @@ -27,6 +27,7 @@ import com.bazaarvoice.bvandroidsdk.BVSDK; import com.bazaarvoice.bvandroidsdk.BazaarEnvironment; import com.bazaarvoice.bvandroidsdk.CurationsImageLoader; +import com.bazaarvoice.bvandroidsdk.IovationFingerprint; import com.bazaarvoice.bvandroidsdk.PinClient; import com.bazaarvoice.bvsdkdemoandroid.configs.DemoClientConfigUtils; import com.bazaarvoice.bvsdkdemoandroid.conversations.DemoConvResponseHandler; @@ -58,7 +59,9 @@ BVSDK provideBvSdk( @Provides @DemoAppScope BVConversationsClient provideConversationsClient(BVSDK bvsdk) { - return new BVConversationsClient(); + return new BVConversationsClient.Builder(bvsdk) + .fingerprintProvider(new IovationFingerprint(bvsdk)) // For North America authentication + .build(); } @Provides @DemoAppScope diff --git a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java index df8a6e73..c8b5cc2c 100644 --- a/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java +++ b/app/src/main/java/com/bazaarvoice/bvsdkdemoandroid/conversations/DemoConvApiPresenter.java @@ -174,15 +174,11 @@ private boolean readyForDemo() { } private void submitReviewWithPhoto(String productId) { - // For clients non-EU clients, iovation SDK is required! - // String blackbox = getBlackbox(getContext().getApplicationContext()); - view.showProgressWithTitle("Submitting Review..."); File localImageFile = demoAssetsUtil.parseImageFileFromAssets("puppy_thumbnail.jpg"); ReviewSubmissionRequest submission = new ReviewSubmissionRequest.Builder(submitAction, productId) -// .fingerPrint(blackbox) // uncomment me when using iovation SDK .userNickname("shazbat") .userEmail("foo@bar.com") .userId("shazbatuser" + Math.random()) // Creating a random user id to avoid duplicated -- FOR TESTING ONLY!!! diff --git a/bvadvertising/src/main/res/values/strings.xml b/bvadvertising/src/main/res/values/strings.xml deleted file mode 100644 index dbd6707d..00000000 --- a/bvadvertising/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - BVSDK Advertising - \ No newline at end of file diff --git a/bvanalytics/src/main/res/values/strings.xml b/bvanalytics/src/main/res/values/strings.xml deleted file mode 100644 index e18e5cbb..00000000 --- a/bvanalytics/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvanalytics - diff --git a/bvauthiovation/.gitignore b/bvauthiovation/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/bvauthiovation/.gitignore @@ -0,0 +1 @@ +/build diff --git a/bvauthiovation/build.gradle b/bvauthiovation/build.gradle new file mode 100644 index 00000000..379f36d8 --- /dev/null +++ b/bvauthiovation/build.gradle @@ -0,0 +1,30 @@ +buildscript { + dependencies { + classpath sdkBuildDep.androidPlugin + classpath sdkBuildDep.credentialsPlugin + classpath sdkBuildDep.jacocoPlugin + } +} + +repositories { + flatDir { + dir 'libs' + } +} + +apply from: rootProject.file('gradle/bvsdk-module-android.gradle') + +dependencies { + compile('com.iovation.deviceprint.lib.DevicePrint:deviceprint-lib-release-2.3.3:2.3.3@aar') + compile project(':bvconversations') + provided sdkDep.okhttp3 + + // Dependencies for local unit tests + testCompile sdkTestDep.junit + testCompile sdkTestDep.mockito + testCompile sdkTestDep.mockWebServer + testCompile sdkTestDep.robolectric +} + +apply from: rootProject.file('gradle/setup-signing.gradle') +apply from: rootProject.file('gradle/gradle-mvn-push.gradle') \ No newline at end of file diff --git a/bvauthiovation/gradle.properties b/bvauthiovation/gradle.properties new file mode 100644 index 00000000..d990ef32 --- /dev/null +++ b/bvauthiovation/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=Bazaarvoice Authentication Iovation Library +POM_ARTIFACT_ID=auth-iovation +POM_PACKAGING=aar diff --git a/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar b/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar new file mode 100644 index 00000000..227a0939 Binary files /dev/null and b/bvauthiovation/libs/deviceprint-lib-release-2.3.3.aar differ diff --git a/bvauthiovation/proguard-rules.pro b/bvauthiovation/proguard-rules.pro new file mode 100644 index 00000000..e107ef53 --- /dev/null +++ b/bvauthiovation/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/casey.kulm/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/bvauthiovation/src/main/AndroidManifest.xml b/bvauthiovation/src/main/AndroidManifest.xml new file mode 100644 index 00000000..081853c9 --- /dev/null +++ b/bvauthiovation/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java b/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java new file mode 100644 index 00000000..4fa78013 --- /dev/null +++ b/bvauthiovation/src/main/java/com/bazaarvoice/bvandroidsdk/IovationFingerprint.java @@ -0,0 +1,25 @@ +package com.bazaarvoice.bvandroidsdk; + +import android.content.Context; + +import com.iovation.mobile.android.DevicePrint; + +public final class IovationFingerprint implements FingerprintProvider { + private final Context appContext; + private String fingerPrint; + private boolean started = false; + + public IovationFingerprint(BVSDK bvsdk) { + this.appContext = bvsdk.getBvUserProvidedData().getAppContext(); + } + + @Override + public String getFingerprint() throws VerifyError { + if (!started) { + DevicePrint.start(appContext); + fingerPrint = DevicePrint.getBlackbox(appContext); + started = true; + } + return fingerPrint; + } +} \ No newline at end of file diff --git a/bvcommon/src/main/res/values/strings.xml b/bvcommon/src/main/res/values/strings.xml index 4a7502b7..10ff95aa 100644 --- a/bvcommon/src/main/res/values/strings.xml +++ b/bvcommon/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - BVSDK Common seen_product_onscreen_with_id_set \ No newline at end of file diff --git a/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java b/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java index 8c924f74..054188fb 100644 --- a/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java +++ b/bvcommon/src/sharedSdkTest/java/com/bazaarvoice/bvandroidsdk/BVBaseTest.java @@ -117,7 +117,7 @@ protected void modifyPropertiesToInitSDK() {} protected void afterInitSdk(BVSDK bvsdk) {} - String jsonFileAsString(String fileName) throws IOException { + static String jsonFileAsString(String fileName) throws IOException { File file = new File("src/test/resources/" + fileName); return readFile(file); } diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java index b139d29b..367087ec 100644 --- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/BVConversationsClient.java @@ -17,7 +17,9 @@ package com.bazaarvoice.bvandroidsdk; -import java.util.List; +import android.os.Looper; + +import com.google.gson.Gson; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -27,13 +29,13 @@ * BVConversationsClient as a single instance in your app. */ public final class BVConversationsClient { - private static final OkHttpClient okHttpClient = BVSDK.getInstance().getBvWorkerData().getOkHttpClient(); - private static final String CONVERSATIONS_RELATIVE_URL = "data/"; - static final String conversationsBaseUrl = BVSDK.getInstance().getBvWorkerData().getRootApiUrls().getBazaarvoiceApiRootUrl() + CONVERSATIONS_RELATIVE_URL; - private final RequestFactory requestFactory; private final BVConfig bvConfig; private final ConversationsAnalyticsManager conversationsAnalyticsManager; + private final Looper bgLooper; + private final Looper uiLooper; + private final OkHttpClient okHttpClient; + private final Gson gson; /** * @deprecated Now use the {@link Builder} which explicitly requires the {@link BVSDK} singleton instance. @@ -45,12 +47,20 @@ public BVConversationsClient() { bvConfig = client.bvConfig; requestFactory = client.requestFactory; conversationsAnalyticsManager = client.conversationsAnalyticsManager; + bgLooper = client.bgLooper; + uiLooper = client.uiLooper; + okHttpClient = client.okHttpClient; + gson = client.gson; } private BVConversationsClient(Builder builder) { bvConfig = builder.bvConfig; requestFactory = builder.requestFactory; conversationsAnalyticsManager = builder.conversationsAnalyticsManager; + bgLooper = builder.bgLooper; + uiLooper = builder.uiLooper; + okHttpClient = builder.okHttpClient; + gson = builder.gson; } /** @@ -58,7 +68,7 @@ private BVConversationsClient(Builder builder) { * @return LoadCall object with the request ready to be sent */ public LoadCallDisplay prepareCall(QuestionAndAnswerRequest request) { - return factoryCreateDisplayCall(QuestionAndAnswerResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(QuestionAndAnswerResponse.class, request); } /** @@ -66,11 +76,11 @@ public LoadCallDisplay prep * @return LoadCall object with the request ready to be sent */ public LoadCallDisplay prepareCall(ProductDisplayPageRequest request) { - return factoryCreateDisplayCall(ProductDisplayPageResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(ProductDisplayPageResponse.class, request); } public LoadCallDisplay prepareCall(BulkProductRequest request) { - return factoryCreateDisplayCall(BulkProductResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(BulkProductResponse.class, request); } /** @@ -78,7 +88,7 @@ public LoadCallDisplay prepareCall(Bulk * @return LoadCall object with the request ready to be sent */ public LoadCallDisplay prepareCall(BulkRatingsRequest request) { - return factoryCreateDisplayCall(BulkRatingsResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(BulkRatingsResponse.class, request); } /** @@ -86,7 +96,7 @@ public LoadCallDisplay prepareCall(Bulk * @return LoadCall object with the request ready to be sent */ public LoadCallDisplay prepareCall(BulkStoreRequest request) { - return factoryCreateDisplayCall(BulkStoreResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(BulkStoreResponse.class, request); } /** @@ -94,19 +104,19 @@ public LoadCallDisplay prepareCall(BulkStor * @return LoadCall object with the request ready to be sent */ public LoadCallDisplay prepareCall(ReviewsRequest request) { - return factoryCreateDisplayCall(ReviewResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(ReviewResponse.class, request); } public LoadCallDisplay prepareCall(StoreReviewsRequest request) { - return factoryCreateDisplayCall(StoreReviewResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(StoreReviewResponse.class, request); } public LoadCallDisplay prepareCall(AuthorsRequest request) { - return factoryCreateDisplayCall(AuthorsResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(AuthorsResponse.class, request); } public LoadCallDisplay prepareCall(CommentsRequest request) { - return factoryCreateDisplayCall(CommentsResponse.class, request, requestFactory, conversationsAnalyticsManager); + return factoryCreateDisplayCall(CommentsResponse.class, request); } public interface DisplayLoader { @@ -114,59 +124,43 @@ public interface DisplayLoader prepareCall(AnswerSubmissionRequest submission) { - return factoryCreateSubmissionCall(AnswerSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations()); + return factoryCreateSubmissionCall(AnswerSubmissionResponse.class, submission); } public LoadCallSubmission prepareCall(ReviewSubmissionRequest submission) { - return factoryCreateSubmissionCall(ReviewSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations()); + return factoryCreateSubmissionCall(ReviewSubmissionResponse.class, submission); } public LoadCallSubmission prepareCall(StoreReviewSubmissionRequest submission) { - return factoryCreateSubmissionCall(StoreReviewSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversationsStores()); + return factoryCreateSubmissionCall(StoreReviewSubmissionResponse.class, submission); } public LoadCallSubmission prepareCall(QuestionSubmissionRequest submission) { - return factoryCreateSubmissionCall(QuestionSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations()); + return factoryCreateSubmissionCall(QuestionSubmissionResponse.class, submission); } public LoadCallSubmission prepareCall(FeedbackSubmissionRequest submission) { - return factoryCreateSubmissionCall(FeedbackSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations()); + return factoryCreateSubmissionCall(FeedbackSubmissionResponse.class, submission); } public LoadCallSubmission prepareCall(CommentSubmissionRequest submission) { - return factoryCreateSubmissionCall(CommentSubmissionResponse.class, submission, requestFactory, conversationsAnalyticsManager, bvConfig.getApiKeyConversations()); + return factoryCreateSubmissionCall(CommentSubmissionResponse.class, submission); } - private static LoadCallDisplay factoryCreateDisplayCall(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager) { + private LoadCallDisplay factoryCreateDisplayCall(Class responseTypeClass, RequestType request) { final Request okRequest = requestFactory.create(request); - return new LoadCallDisplay(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager); + return new LoadCallDisplay(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager, okHttpClient, gson); } - private static LoadCallSubmission factoryCreateSubmissionCall(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) { + private LoadCallSubmission factoryCreateSubmissionCall(Class responseTypeClass, RequestType request) { if (request.getAction() == Action.Submit) { request.setForcePreview(true); } - return loadCallFromSubmission(responseTypeClass, request, requestFactory, conversationsAnalyticsManager, apiKey); + return loadCallFromSubmission(responseTypeClass, request); } - static LoadCallSubmission loadCallFromSubmission(Class responseTypeClass, RequestType request, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) { - final Request okRequest = requestFactory.create(request); - return new LoadCallSubmission(request, responseTypeClass, okHttpClient.newCall(okRequest), conversationsAnalyticsManager, requestFactory, apiKey); - } - - // TODO: Remove as part of photo request refactor - static LoadCall reCreateCallWithPhotos(Class responseTypeClass, RequestType submission, List photos, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) { - submission.setPhotos(photos); - submission.getBuilder().photoUploads.clear(); - submission.setForcePreview(false); - - return loadCallFromSubmission(responseTypeClass, submission, requestFactory, conversationsAnalyticsManager, apiKey); - } - - // TODO: Remove as part of photo request refactor - static LoadCall reCreateCallNoPreview(Class responseTypeClass, RequestType submission, RequestFactory requestFactory, ConversationsAnalyticsManager conversationsAnalyticsManager, String apiKey) { - submission.setForcePreview(false); - return loadCallFromSubmission(responseTypeClass, submission, requestFactory, conversationsAnalyticsManager, apiKey); + private LoadCallSubmission loadCallFromSubmission(Class responseTypeClass, RequestType request) { + return new LoadCallSubmission(request, responseTypeClass, conversationsAnalyticsManager, requestFactory, bgLooper, uiLooper, okHttpClient, gson); } /** @@ -175,12 +169,22 @@ static Request create(RequestType request) { + if (request instanceof ReviewsRequest) { + return createFromReviewRequest((ReviewsRequest) request); + } else if (request instanceof QuestionAndAnswerRequest) { + return createFromQuestionAndAnswerRequest((QuestionAndAnswerRequest) request); + } else if (request instanceof CommentsRequest) { + return createFromCommentsRequest((CommentsRequest) request); + } else if (request instanceof AuthorsRequest) { + return createFromAuthorsRequest((AuthorsRequest) request); + } else if (request instanceof BulkStoreRequest) { + return createFromBulkStoreRequest((BulkStoreRequest) request); + } else if (request instanceof BulkRatingsRequest) { + return createFromBulkRatingsRequest((BulkRatingsRequest) request); + } else if (request instanceof StoreReviewsRequest) { + return createFromStoreReviewsRequest((StoreReviewsRequest) request); + } else if (request instanceof BulkProductRequest) { + return createFromBulkProductRequest((BulkProductRequest) request); + } else if (request instanceof ProductDisplayPageRequest) { + return createFromProductDisplayPageRequest((ProductDisplayPageRequest) request); + } else if (request instanceof FeedbackSubmissionRequest) { + return createFromFeedbackSubmissionRequest((FeedbackSubmissionRequest) request); + } else if (request instanceof ReviewSubmissionRequest) { + return createFromReviewSubmissionRequest((ReviewSubmissionRequest) request); + } else if (request instanceof StoreReviewSubmissionRequest) { + return createFromStoreReviewSubmissionRequest((StoreReviewSubmissionRequest) request); + } else if (request instanceof QuestionSubmissionRequest) { + return createFromQuestionSubmissionRequest((QuestionSubmissionRequest) request); + } else if (request instanceof AnswerSubmissionRequest) { + return createFromAnswerSubmissionRequest((AnswerSubmissionRequest) request); + } else if (request instanceof CommentSubmissionRequest) { + return createFromCommentSubmissionRequest((CommentSubmissionRequest) request); + } else if (request instanceof PhotoUploadRequest) { + return createFromPhotoUploadRequest((PhotoUploadRequest) request); + } + throw new IllegalStateException("Unknown request type: " + request.getClass().getCanonicalName()); + } + + // endregion + + // region Mapping Implementation + private Request createFromReviewRequest(ReviewsRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEWS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); + + if (!request.getReviewIncludeTypes().isEmpty()) { + if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) { + // "Stats=Reviews must be used in conjunction with Include=Products" + // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating) + httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS); + } + String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ","); + httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr); + } + + if (!request.getSorts().isEmpty()) { + httpUrlBuilder + .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); + } + + if (request.getSearchPhrase() != null) { + httpUrlBuilder + .addQueryParameter(kSEARCH, request.getSearchPhrase()); + } + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromQuestionAndAnswerRequest(QuestionAndAnswerRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(QUESTIONS_AND_ANSWERS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); + + httpUrlBuilder.addQueryParameter(kINCLUDE, INCLUDE_ANSWERS); + + if (!request.getQuestionSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); + } + + if (!request.getAnswerSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); + } + + if (request.getSearchPhrase() != null) { + httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase()); + } + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromCommentsRequest(CommentsRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEW_COMMENTS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); + + if (!request.getIncludeTypes().isEmpty()) { + httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludeTypes(), ",")); + } + + if (!request.getIncludeTypeLimitMap().isEmpty()) { + for (CommentIncludeType commentIncludeType : request.getIncludeTypeLimitMap().keySet()) { + String formattedKey = String.format("Limit_%s", commentIncludeType.toString()); + int limit = request.getIncludeTypeLimitMap().get(commentIncludeType); + String formattedValue = String.valueOf(limit); + httpUrlBuilder.addEncodedQueryParameter(formattedKey, formattedValue); + } + } + + if (!request.getSorts().isEmpty()) { + httpUrlBuilder + .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); + } + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromAuthorsRequest(AuthorsRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(AUTHORS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + + if (!request.getReviewSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ",")); + } + + if (!request.getQuestionSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); + } + + if (!request.getAnswerSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); + } + + if (!request.getIncludes().isEmpty()) { + httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ",")); + } + + for (Include include : request.getIncludes()) { + httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit())); + } + + if (!request.getStatistics().isEmpty()) { + httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ",")); + } + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromBulkStoreRequest(BulkStoreRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(PRODUCTS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); + + httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey()); + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromBulkRatingsRequest(BulkRatingsRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEWS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + + httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey()); + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromStoreReviewsRequest(StoreReviewsRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEWS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); + + if (!request.getReviewIncludeTypes().isEmpty()) { + if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) { + // "Stats=Reviews must be used in conjunction with Include=Products" + // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating) + httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS); + } + String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ","); + httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr); + } + + if (!request.getSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); + } + + // TODO: All of these repeated query params are begging for some better composability. Composite or Decorator pattern? + if (request.getSearchPhrase() != null) { + httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase()); + } + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromBulkProductRequest(BulkProductRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(PRODUCTS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addSortableProductParams(httpUrlBuilder, request); + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromProductDisplayPageRequest(ProductDisplayPageRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(PRODUCTS_ENDPOINT); + + addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); + addCommonDisplayQueryParams(httpUrlBuilder, request); + addSortableProductParams(httpUrlBuilder, request); + + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .build(); + } + + private Request createFromFeedbackSubmissionRequest(FeedbackSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(FEEDBACK_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider); + + formPutSafe(formBodyBuilder, kCONTENT_ID, request.getContentId()); + formPutSafe(formBodyBuilder, kCONTENT_TYPE, request.getContentType()); + formPutSafe(formBodyBuilder, kFEEDBACK_TYPE, request.getFeedbackType()); + formPutSafe(formBodyBuilder, kVOTE, request.getFeedbackVote()); + formPutSafe(formBodyBuilder, kREASON_TEXT, request.getReasonFlaggedText()); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromReviewSubmissionRequest(ReviewSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEW_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider); + addCommonReviewSubmissionFormParams(formBodyBuilder, request); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromStoreReviewSubmissionRequest(StoreReviewSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(REVIEW_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, storeApiKey, bvMobileInfo, fingerprintProvider); + addCommonReviewSubmissionFormParams(formBodyBuilder, request); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromQuestionSubmissionRequest(QuestionSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(QUESTION_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider); + formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId()); + formPutSafe(formBodyBuilder, kQUESTION_SUMMARY, request.getQuestionSummary()); + formPutSafe(formBodyBuilder, kQUESTION_DETAILS, request.getQuestionDetails()); + formPutSafe(formBodyBuilder, kIS_ANONUSER, request.getUserAnonymous()); + formPutSafe(formBodyBuilder, kSEND_EMAIL_ANSWERED, request.getSendEmailAlertWhenAnswered()); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromAnswerSubmissionRequest(AnswerSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(ANSWER_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider); + formPutSafe(formBodyBuilder, kQUESTIONID, request.getQuestionId()); + formPutSafe(formBodyBuilder, kANSWERTEXT, request.getAnswerText()); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromCommentSubmissionRequest(CommentSubmissionRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(COMMENT_SUBMIT_ENDPOINT); + + FormBody.Builder formBodyBuilder = new FormBody.Builder(); + + addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo, fingerprintProvider); + formPutSafe(formBodyBuilder, KEY_REVIEW_ID, request.getReviewId()); + formPutSafe(formBodyBuilder, KEY_COMMENT_TEXT, request.getCommentText()); + formPutSafe(formBodyBuilder, KEY_TITLE, request.getTitle()); + + RequestBody requestBody = formBodyBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + + private Request createFromPhotoUploadRequest(PhotoUploadRequest request) { + Request.Builder okRequestBuilder = new Request.Builder(); + + HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) + .newBuilder() + .addPathSegments(PHOTO_SUBMIT_ENDPOINT); + + MultipartBody.Builder multiPartBuilder = new MultipartBody.Builder(); + + PhotoUpload upload = request.getPhotoUpload(); + + multiPartBuilder + .setType(MultipartBody.FORM) + .addFormDataPart(ConversationsRequest.kAPI_VERSION, API_VERSION) + .addFormDataPart(ConversationsRequest.kPASS_KEY, convApiKey) + .addFormDataPart(PhotoUpload.kCONTENT_TYPE, upload.getContentType().getKey()) + .addFormDataPart("photo", "photo.png", RequestBody.create(MEDIA_TYPE_PNG, upload.getPhotoFile())); + + RequestBody requestBody = multiPartBuilder.build(); + HttpUrl httpUrl = httpUrlBuilder.build(); + + Headers.Builder headersBuilder = new Headers.Builder(); + addCommonHeaders(headersBuilder, bvSdkUserAgent); + Headers headers = headersBuilder.build(); + + return okRequestBuilder + .url(httpUrl) + .headers(headers) + .post(requestBody) + .build(); + } + // endregion + + // region Static Helpers + private static void addCommonQueryParams(HttpUrl.Builder httpUrlBuilder, String apiKey, BVMobileInfo bvMobileInfo) { + httpUrlBuilder.addQueryParameter(kAPI_VERSION, API_VERSION) + .addQueryParameter(kPASS_KEY, apiKey) + .addQueryParameter(kAPP_ID, bvMobileInfo.getMobileAppIdentifier()) + .addQueryParameter(kAPP_VERSION, bvMobileInfo.getMobileAppVersion()) + .addQueryParameter(kBUILD_NUM, bvMobileInfo.getMobileAppCode()) + .addQueryParameter(kSDK_VERSION, bvMobileInfo.getBvSdkVersion()); + } + + private static void addCommonPagingQueryParams(HttpUrl.Builder httpUrlBuilder, int limit, int offset) { + httpUrlBuilder.addQueryParameter(kLIMIT, String.valueOf(limit)); + httpUrlBuilder.addQueryParameter(kOFFSET, String.valueOf(offset)); + } + + private static void addCommonDisplayQueryParams(HttpUrl.Builder httpUrlBuilder, ConversationsDisplayRequest request) { + addFilterQueryParams(httpUrlBuilder, request.getFilters()); + addExtraQueryParams(httpUrlBuilder, request.getExtraParams()); + } + + private static void addFilterQueryParams(HttpUrl.Builder httpUrlBuilder, List filters) { + for (Filter filter : filters) { + httpUrlBuilder + .addEncodedQueryParameter(kFILTER, filter.toString()); + } + } + + private static void addExtraQueryParams(HttpUrl.Builder httpUrlBuilder, List extraParams) { + for (ConversationsDisplayRequest.QueryPair queryPair : extraParams) { + if (queryPair.getKey() == null || queryPair.getValue() == null) { + continue; + } + httpUrlBuilder + .addQueryParameter(queryPair.getKey(), queryPair.getValue()); + } + } + + private static void addCommonHeaders(Headers.Builder headersBuilder, String bvSdkUserAgent) { + headersBuilder.add(KEY_USER_AGENT, bvSdkUserAgent); + } + + private static void addSortableProductParams(HttpUrl.Builder httpUrlBuilder, SortableProductRequest request) { + if (!request.getReviewSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ",")); + } + + if (!request.getQuestionSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); + } + + if (!request.getAnswerSorts().isEmpty()){ + httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); + } + + if (!request.getIncludes().isEmpty()) { + httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ",")); + } + + for (Include include : request.getIncludes()) { + httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit())); + } + + if (!request.getStatistics().isEmpty()) { + httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ",")); + } + } + + private static void formPutSafe(FormBody.Builder formBodyBuilder, @NonNull String key, @Nullable Object value) { + if (value != null && !value.toString().isEmpty()) { + String valueStr = String.valueOf(value); + formBodyBuilder.add(key, valueStr); + } + } + + private static void addCommonSubmissionFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request, String apiKey, BVMobileInfo bvMobileInfo, FingerprintProvider fingerprintProvider) { + formPutSafe(formBodyBuilder, kAPI_VERSION, API_VERSION); + formPutSafe(formBodyBuilder, kPASS_KEY, apiKey); + formPutSafe(formBodyBuilder, kAPP_ID, bvMobileInfo.getMobileAppIdentifier()); + formPutSafe(formBodyBuilder, kAPP_VERSION, bvMobileInfo.getMobileAppVersion()); + formPutSafe(formBodyBuilder, kBUILD_NUM, bvMobileInfo.getMobileAppCode()); + formPutSafe(formBodyBuilder, kSDK_VERSION, bvMobileInfo.getBvSdkVersion()); + formPutSafe(formBodyBuilder, kCAMPAIGN_ID, request.getCampaignId()); + formPutSafe(formBodyBuilder, kFINGER_PRINT, fingerprintProvider.getFingerprint()); + formPutSafe(formBodyBuilder, kHOSTED_AUTH_EMAIL, request.getHostedAuthenticationEmail()); + formPutSafe(formBodyBuilder, kHOST_AUTH_CALLBACK, request.getHostedAuthenticationCallback()); + formPutSafe(formBodyBuilder, kLOCALE, request.getLocale()); + formPutSafe(formBodyBuilder, kUSER, request.getUser()); + formPutSafe(formBodyBuilder, kUSER_EMAIL, request.getUserEmail()); + formPutSafe(formBodyBuilder, kUSER_ID, request.getUserId()); + formPutSafe(formBodyBuilder, kUSER_LOCATION, request.getUserLocation()); + formPutSafe(formBodyBuilder, kUSER_NICKNAME, request.getUserNickname()); + formPutSafe(formBodyBuilder, kSEND_EMAIL_PUBLISHED, request.getSendEmailAlertWhenPublished()); + formPutSafe(formBodyBuilder, kAGREE_TERMS, request.getAgreedToTermsAndConditions()); + formPutSafe(formBodyBuilder, kACTION, getAction(request).getKey()); + + addSubmissionPhotosFormParams(formBodyBuilder, request); + } + + private static void addSubmissionPhotosFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request) { + final List photos = request.getPhotos(); + if (photos != null) { + int idx = 0; + for (Photo photo : photos) { + final String keyPhotoUrl = String.format(Locale.US, KEY_PHOTO_URL_TEMPLATE, idx); + final String keyPhotoCaption = String.format(Locale.US, KEY_PHOTO_CAPTION_TEMPLATE, idx); + formPutSafe(formBodyBuilder, keyPhotoUrl, photo.getContent().getNormalUrl()); + formPutSafe(formBodyBuilder, keyPhotoCaption, photo.getCaption()); + idx++; + } + } + } + + private static void addCommonReviewSubmissionFormParams(FormBody.Builder formBodyBuilder, BaseReviewSubmissionRequest request) { + formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId()); + formPutSafe(formBodyBuilder, kIS_RECOMMENDED, request.getRecommended()); + formPutSafe(formBodyBuilder, kSEND_EMAIL_COMMENTED, request.getSendEmailAlertWhenCommented()); + formPutSafe(formBodyBuilder, kRATING, request.getRating()); + formPutSafe(formBodyBuilder, kNET_PROMOTER_SCORE, request.getNetPromoterScore()); + formPutSafe(formBodyBuilder, kNET_PROMOTER_COMMENT, request.getNetPromoterComment()); + formPutSafe(formBodyBuilder, kTITLE, request.getTitle()); + formPutSafe(formBodyBuilder, kREVIEW_TEXT, request.getReviewText()); + + final List predefinedTags = request.getPredefinedTags(); + for (int i=0; i freeFormTags = request.getFreeFormTags(); + final List freeFormTagKeys = new ArrayList<>(freeFormTags.keySet()); + for (int i=0; i ratingSliderKeys = request.getRatingSliders().keySet(); + for (String key : ratingSliderKeys) { + final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key); + formPutSafe(formBodyBuilder, keyRating, request.getRatingSliders().get(key)); + } + + final Set ratingQuestionsKeys = request.getRatingQuestions().keySet(); + for (String key : ratingQuestionsKeys) { + final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key); + formPutSafe(formBodyBuilder, keyRating, request.getRatingQuestions().get(key)); + } + + final Set contextDataValueKeys = request.getContextDataValues().keySet(); + for (String key : contextDataValueKeys) { + final String keyRating = String.format(Locale.US, KEY_CDV_TEMPLATE, key); + formPutSafe(formBodyBuilder, keyRating, request.getContextDataValues().get(key)); + } + + final Map additionalFields = request.getAdditionalFields(); + for (String key : additionalFields.keySet()) { + final String value = additionalFields.get(key); + final String keyAdditionalField = String.format(Locale.US, KEY_ADDITIONAL_PARAM_TEMPLATE, key); + formPutSafe(formBodyBuilder, keyAdditionalField, value); + } + + final List videoDataList = request.getVideoSubmissionData(); + for (int i=0; i { - final Call call; + Call call; final Class responseTypeClass; - final OkHttpClient okHttpClient = BVSDK.getInstance().getBvWorkerData().getOkHttpClient(); - final Gson gson = BVSDK.getInstance().getBvWorkerData().getGson(); + final OkHttpClient okHttpClient; + final Gson gson; final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png"); - LoadCall(Class responseTypeClass, Call call) { - this.call = call; + LoadCall(Class responseTypeClass, OkHttpClient okHttpClient, Gson gson) { this.responseTypeClass = responseTypeClass; + this.okHttpClient = okHttpClient; + this.gson = gson; } /** @@ -60,7 +61,9 @@ abstract class LoadCall conversationsCallback); public void cancel() { - call.cancel(); + if (call != null && !call.isCanceled()) { + call.cancel(); + } } ResponseType deserializeAndCloseResponse(Response response) throws BazaarException { @@ -70,7 +73,6 @@ ResponseType deserializeAndCloseResponse(Response response) throws BazaarExcepti try { Reader jsonReader = response.body().charStream(); conversationResponse = (ResponseType) gson.fromJson(jsonReader, responseTypeClass); - } catch (JsonSyntaxException | JsonIOException e) { error = new BazaarException("Unable to parse JSON " + response); } finally { diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java index e7cd0ec9..33daf20c 100644 --- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallDisplay.java @@ -17,10 +17,13 @@ package com.bazaarvoice.bvandroidsdk; +import com.google.gson.Gson; + import java.io.IOException; import okhttp3.Call; import okhttp3.Callback; +import okhttp3.OkHttpClient; import okhttp3.Response; import static com.bazaarvoice.bvandroidsdk.internal.Utils.checkMain; @@ -85,8 +88,9 @@ public void onResponse(Call call, Response response) throws IOException { } } - LoadCallDisplay(RequestType request, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager) { - super(responseTypeClass, call); + LoadCallDisplay(RequestType request, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager, OkHttpClient okHttpClient, Gson gson) { + super(responseTypeClass, okHttpClient, gson); + this.call = call; this.request = request; this.conversationsAnalyticsManager = conversationsAnalyticsManager; } diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java index f86f314d..f340b916 100644 --- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/LoadCallSubmission.java @@ -17,20 +17,27 @@ package com.bazaarvoice.bvandroidsdk; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.annotation.AnyThread; +import android.support.annotation.MainThread; +import android.support.annotation.WorkerThread; + +import com.google.gson.Gson; + import java.io.IOException; import java.io.Reader; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; import okhttp3.Request; -import okhttp3.RequestBody; import okhttp3.Response; import static android.util.Log.d; +import static com.bazaarvoice.bvandroidsdk.internal.Utils.checkNotMain; /** * Wrapper for a {@link Call} for a Conversations request to @@ -43,129 +50,153 @@ public final class LoadCallSubmission implements Callback { - private final ConversationsCallback conversationsCallback; - private final LoadCallSubmission loadCallSubmission; - private final RequestType submissionRequest; - private final ConversationsAnalyticsManager conversationsAnalyticsManager; - private final Class responseTypeClass; - private final RequestFactory requestFactory; - private final String apiKey; - - public SubmissionDelegateCallback(final ConversationsCallback conversationsCallback, final LoadCallSubmission loadCallDisplay, final RequestType submissionRequest, final Class responseTypeClass, ConversationsAnalyticsManager conversationsAnalyticsManager, RequestFactory requestFactory, String apiKey) { - this.conversationsCallback = conversationsCallback; - this.loadCallSubmission = loadCallDisplay; - this.submissionRequest = submissionRequest; - this.responseTypeClass = responseTypeClass; - this.conversationsAnalyticsManager = conversationsAnalyticsManager; - this.requestFactory = requestFactory; - this.apiKey = apiKey; - } - - @Override - public void onFailure(Call call, IOException e) { - BazaarException bazaarException = new BazaarException("Submission Request Failed", e); - loadCallSubmission.errorOnMainThread(conversationsCallback, bazaarException); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - ConversationsResponse conversationResponse = null; - BazaarException error = null; - try { - if (!response.isSuccessful()) { - error = new BazaarException("Unsuccessful response for Conversations with error code: " + response.code()); - } else { - try { - conversationResponse = loadCallSubmission.deserializeAndCloseResponse(response); - } catch (BazaarException t) { - error = t; - } - } - - boolean isPreview = submissionRequest.getAction() == Action.Preview; - if (error != null) { - loadCallSubmission.errorOnMainThread(conversationsCallback, error); - } else { - //if User intended to only Preview or if a Submit has already been previewed we are done and can callback - boolean readyToDeliverResult = isPreview || !submissionRequest.getForcePreview(); - if (readyToDeliverResult) { - conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest); - loadCallSubmission.successOnMainThread(conversationsCallback, conversationResponse); - } - //We know that a Submit was succesfully previewed so now we Submit for real - else { - LoadCall newCall = BVConversationsClient.reCreateCallNoPreview(responseTypeClass, submissionRequest, requestFactory, conversationsAnalyticsManager, apiKey); - newCall.loadAsync(conversationsCallback); - } - } - } finally { - if (response != null) { - response.body().close(); - } - } - } - } - - LoadCallSubmission(RequestType submissionRequest, Class responseTypeClass, Call call, ConversationsAnalyticsManager conversationsAnalyticsManager, RequestFactory requestFactory, String apiKey) { - super(responseTypeClass, call); + private final SubmitUiHandler submitUiHandler; + private final SubmitWorkerHandler submitWorkerHandler; + private ConversationsCallback submitCallback; + + LoadCallSubmission( + RequestType submissionRequest, + Class responseTypeClass, + ConversationsAnalyticsManager conversationsAnalyticsManager, + RequestFactory requestFactory, + Looper bgLooper, + Looper uiLooper, + OkHttpClient okHttpClient, + Gson gson) { + super(responseTypeClass, okHttpClient, gson); this.submissionRequest = submissionRequest; this.conversationsAnalyticsManager = conversationsAnalyticsManager; this.requestFactory = requestFactory; - this.apiKey = apiKey; + this.submitUiHandler = new SubmitUiHandler<>(uiLooper, this); + this.submitWorkerHandler = new SubmitWorkerHandler<>(bgLooper, this); } + @WorkerThread @Override public ResponseType loadSync() throws BazaarException { - ConversationsResponse conversationResponse = null; + checkNotMain(); + return submitFlow(true); + } + + /** + * @deprecated To be replaced + *

+ * This is a container for the old load async behavior which is not ideal + * in that it doesn't route 100% of errors to the error block, therefore + * requiring complicated logic to know when success has happened. This is + * being maintained to avoid breaking 6.x clients, but will be replaced by + * the more expected {@link #loadSync()} behavior which routes all errors + * to the error block. + * + * @return Response with expected type + * @throws BazaarException + */ + private ResponseType legacyLoadAsyncBehavior() throws BazaarException { + return submitFlow(false); + } + + /** + * Handles the logic of Preview vs Submit, as well as Photo Submission. + *

+ * If it is Preview mode, then it will simply run, and return. This can be seen + * as a Form preview request. + *

+ * If it is Submit mode, then it will first run in Preview mode to validate + * the form data. If the form data is invalid it will return or throw depending + * on {@code routeFormErrorsToFailure}. If the form data is valid, then it will + * submit each of the photos, collecting the metadata associated with each. If any + * photo submission failed, then an exception will be thrown. If all photo submit + * requests succeeded, it will add any photo metadata to the request, and submit + * in Submit mode. + * + * @param routeFormErrorsToFailure Behavior that each {@link #loadSync()} and {@link #loadAsync(ConversationsCallback)} were + * doing differently. Will go away in a future release, and all errors will route to failure. + * @return Server JSON response mapped to the corresponding POJO type + * @throws BazaarException Exception if the request failed to happen, or if the JSON deserialization failed + */ + private ResponseType submitFlow(boolean routeFormErrorsToFailure) throws BazaarException { BazaarException error = submissionRequest.getError(); if (error != null) { + // If request contains known errors throw error; } - try { - List photoUploads = submissionRequest.getBuilder().photoUploads; - if (photoUploads != null && photoUploads.size() > 0 && !submissionRequest.getForcePreview() && submissionRequest.getAction() == Action.Submit) { + boolean isPreview = submissionRequest.getAction() == Action.Preview; + if (isPreview) { + // If the user chose preview, simply submit request + return submit(); + } else { + // Send preview request for user incase we need to see error values + submissionRequest.setForcePreview(true); + ResponseType previewResponse = submit(); + if (previewResponse.getHasErrors()) { + if (routeFormErrorsToFailure) { + throw new BazaarException(gson.toJson(previewResponse.getErrors())); + } else { + return previewResponse; + } + } - return postPhotosAndSubmissionSync(photoUploads, submissionRequest); + // If it is a valid request, follow through with a full submit + if (submissionRequest.getPhotoUploads() != null && submissionRequest.getPhotoUploads().size() > 0) { + // If the user wants to submit photos, submit each of them, collect the metadata, + // and associate it with the submission request + List photos = postPhotosAndSubmissionSync(submissionRequest.getPhotoUploads()); + submissionRequest.setPhotos(photos); + } + // Toggle back to no be force preview anymore + submissionRequest.setForcePreview(false); + return submit(); + } + } + /** + * @return Server JSON response mapped to the corresponding POJO type + * @throws BazaarException Exception if the request failed to happen, or if the JSON deserialization failed + */ + private ResponseType submit() throws BazaarException { + ResponseType conversationResponse = null; + BazaarException error = null; + final Request okRequest = requestFactory.create(submissionRequest); + call = okHttpClient.newCall(okRequest); + Response response = null; + try { + response = call.execute(); + if (!response.isSuccessful()) { + error = new BazaarException("Unsuccessful response for Conversations with error code: " + response.code()); } else { - Response response = null; try { - response = call.execute(); conversationResponse = deserializeAndCloseResponse(response); - if (conversationResponse.getHasErrors()) { - throw new BazaarException(gson.toJson(conversationResponse.getErrors())); - } - if (submissionRequest.getForcePreview()) { - LoadCall loadCall = BVConversationsClient.reCreateCallNoPreview(responseTypeClass, submissionRequest, requestFactory, conversationsAnalyticsManager, apiKey); - ResponseType finalResponse = (ResponseType) loadCall.loadSync(); - conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest); - return finalResponse; - } - } finally { - if (response != null) { - response.body().close(); - } + } catch (BazaarException t) { + error = t; } } - } catch (Throwable t) { - throw new BazaarException(t.getMessage()); + if (error != null) { + throw error; + } else { + conversationsAnalyticsManager.sendSuccessfulConversationsSubmitResponse(submissionRequest); + return conversationResponse; + } + } catch (IOException e) { + throw new BazaarException("Network request failed", e); + } finally { + if (response != null) { + response.body().close(); + } } - return (ResponseType) conversationResponse; } - private ResponseType postPhotosAndSubmissionSync(List photoUploads, ConversationsSubmissionRequest submission) throws BazaarException { + private List postPhotosAndSubmissionSync(List photoUploads) throws BazaarException { d("Submission", String.format("Preparing to submit %d photos", photoUploads.size())); final List photos = new ArrayList<>(); try { for (PhotoUpload upload : photoUploads) { - Call photoCall = makePhotoCall(upload, submission); + final PhotoUploadRequest uploadRequest = new PhotoUploadRequest.Builder(upload).build(); + final Request okRequest = requestFactory.create(uploadRequest); + Call photoCall = okHttpClient.newCall(okRequest); Response response = null; try { response = photoCall.execute(); @@ -182,48 +213,7 @@ private ResponseType postPhotosAndSubmissionSync(List photoUploads, throw new BazaarException(e.getMessage()); } - LoadCall loadCall = BVConversationsClient.reCreateCallWithPhotos(responseTypeClass, submission, photos, requestFactory, conversationsAnalyticsManager, apiKey); - return (ResponseType) loadCall.loadSync(); - } - - private void postPhotosAndSubmissionAsync(final ConversationsCallback conversationsCallback, final List photoUploads, final ConversationsSubmissionRequest submission) { - final BVLogger bvLogger = BVSDK.getInstance().getBvLogger(); - bvLogger.d("Submission", String.format("Preparing to submit %d photos", photoUploads.size())); - final List photos = Collections.synchronizedList(new ArrayList()); - - for (final PhotoUpload upload : photoUploads) { - Call photoCall = makePhotoCall(upload, submission); - //async upload all photos - photoCall.enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - errorOnMainThread(conversationsCallback, new BazaarException(e.getMessage())); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try { - Photo photo = deserializePhotoResponse(response); - photo.setCaption(upload.getCaption()); - photos.add(photo); - conversationsAnalyticsManager.sendSuccessfulConversationsPhotoUpload(submission); - //whenever we have received successful responses for every expect photo upload we can - // reconstruct the submissionRequest request with the photo upload response details - if (photos.size() == photoUploads.size()) { - LoadCall loadCall = BVConversationsClient.reCreateCallWithPhotos(responseTypeClass, submission, photos, requestFactory, conversationsAnalyticsManager, apiKey); - loadCall.loadAsync(conversationsCallback); - } - } catch (BazaarException e) { - errorOnMainThread(conversationsCallback, e); - } finally { - if (response != null) { - response.body().close(); - } - } - } - }); - } - + return photos; } private Photo deserializePhotoResponse(Response response) throws BazaarException { @@ -238,43 +228,97 @@ private Photo deserializePhotoResponse(Response response) throws BazaarException @Override public void loadAsync(final ConversationsCallback conversationsCallback) { - BazaarException error = submissionRequest.getError(); + submitCallback = conversationsCallback; + dispatchSubmit(); + } - if (error != null) { - errorOnMainThread(conversationsCallback, error); - return; + private static class SubmitUiHandler extends Handler { + private static final int CB_SUCCESS = 1; + private static final int CB_FAILURE = 2; + + private final LoadCallSubmission loadCallSubmission; + + public SubmitUiHandler(Looper looper, LoadCallSubmission loadCallSubmission) { + super(looper); + this.loadCallSubmission = loadCallSubmission; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CB_SUCCESS: { + ResponseType response = (ResponseType) msg.obj; + loadCallSubmission.completeWithSuccess(response); + break; + } + case CB_FAILURE: { + BazaarException bazaarException = (BazaarException) msg.obj; + loadCallSubmission.completeWithFailure(bazaarException); + break; + } + } } + } - List photoUploads = submissionRequest.getBuilder().photoUploads; - //At this point we assume know that since we are submitting and not being forced to preview that it has been previewed already. - // The request is ready to have its photos uploaded then submitted. - if (photoUploads != null && photoUploads.size() > 0 && !submissionRequest.getForcePreview() && submissionRequest.getAction() == Action.Submit) { + private static class SubmitWorkerHandler extends Handler { + private static final int SUBMIT = 1; + private final LoadCallSubmission loadCallSubmission; - postPhotosAndSubmissionAsync(conversationsCallback, photoUploads, submissionRequest); + public SubmitWorkerHandler(Looper looper, LoadCallSubmission loadCallSubmission) { + super(looper); + this.loadCallSubmission = loadCallSubmission; + } - } else { - //At this point we know that the submissionRequest needs to be previewed - //Either by user asking for preview or being forced to preview before Submit - //Or it is ready for full submissionRequest (it's had it's photo's uploaded or never had any) - this.call.enqueue(new SubmissionDelegateCallback<>(conversationsCallback, this, submissionRequest, responseTypeClass, conversationsAnalyticsManager, requestFactory, apiKey)); + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SUBMIT: { + try { + ResponseType response = loadCallSubmission.legacyLoadAsyncBehavior(); + loadCallSubmission.dispatchCompleteWithSuccess(response); + } catch (BazaarException e) { + loadCallSubmission.dispatchCompleteWithFailure(e); + } + break; + } + } } } - // TODO: Clean up the photo submission process to be it's own internal Request object that we send synchronously on our BVSDK HandlerThread - private Call makePhotoCall(PhotoUpload upload, ConversationsSubmissionRequest conversationsSubmitRequest) { - String fullUrl = String.format("%s%s", BVConversationsClient.conversationsBaseUrl, upload.getEndPoint()); - RequestBody req = new MultipartBody.Builder().setType(MultipartBody.FORM) - .addFormDataPart(ConversationsRequest.kAPI_VERSION, ConversationsRequest.API_VERSION) - .addFormDataPart(ConversationsRequest.kPASS_KEY, apiKey) - .addFormDataPart(PhotoUpload.kCONTENT_TYPE, upload.getContentType().getKey()) - .addFormDataPart("photo", "photo.png", RequestBody.create(MEDIA_TYPE_PNG, upload.getPhotoFile())).build(); - - Request request = new Request.Builder() - .addHeader("User-Agent", BVSDK.getInstance().getBvWorkerData().getBvSdkUserAgent()) - .url(fullUrl) - .post(req) - .build(); - - return okHttpClient.newCall(request); + @AnyThread + private void dispatchSubmit() { + submitWorkerHandler.sendMessage(submitWorkerHandler.obtainMessage(SubmitWorkerHandler.SUBMIT)); + } + + @WorkerThread + private void dispatchCompleteWithFailure(BazaarException e) { + submitUiHandler.sendMessage(submitUiHandler.obtainMessage(SubmitUiHandler.CB_FAILURE, e)); + } + + @WorkerThread + private void dispatchCompleteWithSuccess(ResponseType response) { + submitUiHandler.sendMessage(submitUiHandler.obtainMessage(SubmitUiHandler.CB_SUCCESS, response)); + } + + @MainThread + private void completeWithFailure(BazaarException e) { + if (submitCallback != null) { + submitCallback.onFailure(e); + submitCallback = null; + } + } + + @MainThread + private void completeWithSuccess(ResponseType response) { + if (submitCallback != null) { + submitCallback.onSuccess(response); + submitCallback = null; + } + } + + @Override + public void cancel() { + super.cancel(); + submitCallback = null; } } diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java new file mode 100644 index 00000000..19e3ab0d --- /dev/null +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/PhotoUploadRequest.java @@ -0,0 +1,33 @@ +package com.bazaarvoice.bvandroidsdk; + +/** + * Internal class for uploading photos associate with CGC submission + */ +class PhotoUploadRequest extends ConversationsRequest { + private final PhotoUpload photoUpload; + + private PhotoUploadRequest(Builder builder) { + photoUpload = builder.photoUpload; + } + + @Override + BazaarException getError() { + return null; + } + + public PhotoUpload getPhotoUpload() { + return photoUpload; + } + + public static class Builder { + private final PhotoUpload photoUpload; + + public Builder(PhotoUpload photoUpload) { + this.photoUpload = photoUpload; + } + + public PhotoUploadRequest build() { + return new PhotoUploadRequest(this); + } + } +} diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java index 03a6a290..4ef2ea80 100644 --- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/RequestFactory.java @@ -1,858 +1,7 @@ package com.bazaarvoice.bvandroidsdk; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import okhttp3.FormBody; -import okhttp3.Headers; -import okhttp3.HttpUrl; import okhttp3.Request; -import okhttp3.RequestBody; - -/** - * Converts user created Request Objects into OkHttp Request Objects. - * Each user created Request Object will only have the information that - * the user entered. This Factory will manage adding anything else - * required including Headers and Query Parameters, as well as taking - * user inputted data, and formatting it correctly such as POST body - * media content. - */ -class RequestFactory { - // region Generic Request Keys - private static final String kAPI_VERSION = "apiversion"; - private static final String kPASS_KEY = "passkey"; - private static final String kAPP_ID = "_appId"; - private static final String kAPP_VERSION = "_appVersion"; - private static final String kBUILD_NUM = "_buildNumber"; - private static final String kSDK_VERSION = "_bvAndroidSdkVersion"; - private static final String API_VERSION = "5.4"; - private static final String KEY_USER_AGENT = "User-Agent"; - // endregion - - // region Generic Display Request Keys - private static final String kFILTER = "Filter"; - private static final String kSORT= "Sort"; - private static final String kSORT_REVIEW = "Sort_Reviews"; - private static final String kSORT_QUESTIONS = "Sort_Questions"; - private static final String kSORT_ANSWERS= "Sort_Answers"; - private static final String kINCLUDE = "Include"; - private static final String kSTATS = "Stats"; - private static final String kLIMIT = "Limit"; - private static final String kOFFSET = "Offset"; - private static final String kSEARCH = "Search"; - // endregion - - // region Display Reviews Request Keys - private static final String INCLUDE_ANSWERS = "Answers"; - private static final String REVIEWS_ENDPOINT = "data/reviews.json"; - private static final String KEY_STATS = "Stats"; - private static final String STATS_REVIEWS = "Reviews"; - // endregion - - // region Display Q&A Request Keys - private static final String QUESTIONS_AND_ANSWERS_ENDPOINT = "data/questions.json"; - // endregion - - // region Display Comments Request Keys - private static final String REVIEW_COMMENTS_ENDPOINT = "data/reviewcomments.json"; - // endregion - - // region Display Authors Request Keys - private static final String AUTHORS_ENDPOINT = "data/authors.json"; - // endregion - - // region Display Products Request Keys - private static final String PRODUCTS_ENDPOINT = "data/products.json"; - // endregion - - // region Generic Submit Request Keys - private static final String kCAMPAIGN_ID = "campaignid"; - private static final String kFINGER_PRINT = "fp"; - private static final String kHOSTED_AUTH_EMAIL = "hostedauthentication_authenticationemail"; - private static final String kHOST_AUTH_CALLBACK = "hostedauthentication_callbackurl"; - private static final String kLOCALE = "locale"; - private static final String kUSER = "User"; - private static final String kUSER_EMAIL = "UserEmail"; - private static final String kUSER_LOCATION = "UserLocation"; - private static final String kUSER_NICKNAME = "UserNickname"; - private static final String kSEND_EMAIL_PUBLISHED = "sendemailalertwhenpublished"; - private static final String kAGREE_TERMS = "agreedToTermsAndConditions"; - private static final String kACTION = "action"; - private static final String KEY_PHOTO_URL_TEMPLATE = "photourl_%d"; - private static final String KEY_PHOTO_CAPTION_TEMPLATE = "photocaption_%d"; - private static final String kPRODUCT_ID = "ProductId"; - // endregion - - // region Submit Feedback Request Keys - private static final String FEEDBACK_SUBMIT_ENDPOINT = "data/submitfeedback.json"; - private static final String kCONTENT_ID = "ContentId"; - private static final String kCONTENT_TYPE = "ContentType"; - private static final String kFEEDBACK_TYPE = "FeedbackType"; - private static final String kUSER_ID = "UserId"; - private static final String kVOTE = "Vote"; - private static final String kREASON_TEXT = "ReasonText"; - // endregion - - // region Submit Review Request Keys - private static final String REVIEW_SUBMIT_ENDPOINT = "data/submitreview.json"; - private static final String KEY_ADDITIONAL_PARAM_TEMPLATE = "additionalfield_%s"; - private static final String kIS_RECOMMENDED = "IsRecommended"; - private static final String kSEND_EMAIL_COMMENTED = "SendEmailAlertWhenCommented"; - private static final String kRATING = "Rating"; - private static final String kNET_PROMOTER_SCORE = "NetPromoterScore"; - private static final String kTITLE = "Title"; - private static final String kREVIEW_TEXT = "ReviewText"; - private static final String kNET_PROMOTER_COMMENT = "NetPromoterComment"; - private static final String VIDEO_URL_TEMPLATE = "VideoUrl_%d"; - private static final String VIDEO_CAPTION_TEMPLATE = "VideoCaption_%d"; - private static final String KEY_RATING_TEMPLATE = "rating_%s"; - private static final String KEY_CDV_TEMPLATE = "contextdatavalue_%s"; - private static final String KEY_FREEFORM_TAG_TEMPLATE = "tag_%s_%d"; - // endregion - - // region Submit Question Request Keys - private static final String QUESTION_SUBMIT_ENDPOINT = "data/submitquestion.json"; - private static final String kQUESTION_SUMMARY = "QuestionSummary"; - private static final String kQUESTION_DETAILS = "QuestionDetails"; - private static final String kIS_ANONUSER = "IsUserAnonymous"; - private static final String kSEND_EMAIL_ANSWERED = "SendEmailAlertWhenAnswered"; - // endregion - - // region Submit Answer Request Keys - private static final String ANSWER_SUBMIT_ENDPOINT = "data/submitanswer.json"; - private static final String kQUESTIONID = "QuestionId"; - private static final String kANSWERTEXT = "AnswerText"; - // endregion - - // region Submit Comment Request Keys - private static final String COMMENT_SUBMIT_ENDPOINT = "data/submitreviewcomment.json"; - private static final String KEY_REVIEW_ID = "ReviewId"; - private static final String KEY_COMMENT_TEXT = "CommentText"; - private static final String KEY_TITLE = "Title"; - // endregion - - // region Instance Fields - private final BVMobileInfo bvMobileInfo; - private final String bvRootApiUrl; - private final String convApiKey; - private final String storeApiKey; - private final String bvSdkUserAgent; - // endregion - - // region Constructor - RequestFactory(BVMobileInfo bvMobileInfo, BVRootApiUrls bvRootApiUrls, BVConfig bvConfig, String bvSdkUserAgent) { - this.bvMobileInfo = bvMobileInfo; - this.bvRootApiUrl = bvRootApiUrls.getBazaarvoiceApiRootUrl(); - this.convApiKey = bvConfig.getApiKeyConversations(); - this.storeApiKey = bvConfig.getApiKeyConversationsStores(); - this.bvSdkUserAgent = bvSdkUserAgent; - } - // endregion - - // region API - Request create(RequestType request) { - if (request instanceof ReviewsRequest) { - return createFromReviewRequest((ReviewsRequest) request); - } else if (request instanceof QuestionAndAnswerRequest) { - return createFromQuestionAndAnswerRequest((QuestionAndAnswerRequest) request); - } else if (request instanceof CommentsRequest) { - return createFromCommentsRequest((CommentsRequest) request); - } else if (request instanceof AuthorsRequest) { - return createFromAuthorsRequest((AuthorsRequest) request); - } else if (request instanceof BulkStoreRequest) { - return createFromBulkStoreRequest((BulkStoreRequest) request); - } else if (request instanceof BulkRatingsRequest) { - return createFromBulkRatingsRequest((BulkRatingsRequest) request); - } else if (request instanceof StoreReviewsRequest) { - return createFromStoreReviewsRequest((StoreReviewsRequest) request); - } else if (request instanceof BulkProductRequest) { - return createFromBulkProductRequest((BulkProductRequest) request); - } else if (request instanceof ProductDisplayPageRequest) { - return createFromProductDisplayPageRequest((ProductDisplayPageRequest) request); - } else if (request instanceof FeedbackSubmissionRequest) { - return createFromFeedbackSubmissionRequest((FeedbackSubmissionRequest) request); - } else if (request instanceof ReviewSubmissionRequest) { - return createFromReviewSubmissionRequest((ReviewSubmissionRequest) request); - } else if (request instanceof StoreReviewSubmissionRequest) { - return createFromStoreReviewSubmissionRequest((StoreReviewSubmissionRequest) request); - } else if (request instanceof QuestionSubmissionRequest) { - return createFromQuestionSubmissionRequest((QuestionSubmissionRequest) request); - } else if (request instanceof AnswerSubmissionRequest) { - return createFromAnswerSubmissionRequest((AnswerSubmissionRequest) request); - } else if (request instanceof CommentSubmissionRequest) { - return createFromCommentSubmissionRequest((CommentSubmissionRequest) request); - } - throw new IllegalStateException("Unknown request type: " + request.getClass().getCanonicalName()); - } - - // endregion - - // region Mapping Implementation - private Request createFromReviewRequest(ReviewsRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEWS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); - - if (!request.getReviewIncludeTypes().isEmpty()) { - if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) { - // "Stats=Reviews must be used in conjunction with Include=Products" - // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating) - httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS); - } - String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ","); - httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr); - } - - if (!request.getSorts().isEmpty()) { - httpUrlBuilder - .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); - } - - if (request.getSearchPhrase() != null) { - httpUrlBuilder - .addQueryParameter(kSEARCH, request.getSearchPhrase()); - } - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromQuestionAndAnswerRequest(QuestionAndAnswerRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(QUESTIONS_AND_ANSWERS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); - - httpUrlBuilder.addQueryParameter(kINCLUDE, INCLUDE_ANSWERS); - - if (!request.getQuestionSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); - } - - if (!request.getAnswerSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); - } - - if (request.getSearchPhrase() != null) { - httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase()); - } - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromCommentsRequest(CommentsRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEW_COMMENTS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); - - if (!request.getIncludeTypes().isEmpty()) { - httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludeTypes(), ",")); - } - - if (!request.getIncludeTypeLimitMap().isEmpty()) { - for (CommentIncludeType commentIncludeType : request.getIncludeTypeLimitMap().keySet()) { - String formattedKey = String.format("Limit_%s", commentIncludeType.toString()); - int limit = request.getIncludeTypeLimitMap().get(commentIncludeType); - String formattedValue = String.valueOf(limit); - httpUrlBuilder.addEncodedQueryParameter(formattedKey, formattedValue); - } - } - - if (!request.getSorts().isEmpty()) { - httpUrlBuilder - .addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); - } - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromAuthorsRequest(AuthorsRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(AUTHORS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - - if (!request.getReviewSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ",")); - } - - if (!request.getQuestionSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); - } - - if (!request.getAnswerSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); - } - - if (!request.getIncludes().isEmpty()) { - httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ",")); - } - - for (Include include : request.getIncludes()) { - httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit())); - } - - if (!request.getStatistics().isEmpty()) { - httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ",")); - } - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromBulkStoreRequest(BulkStoreRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(PRODUCTS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); - - httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey()); - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromBulkRatingsRequest(BulkRatingsRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEWS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - - httpUrlBuilder.addQueryParameter(kSTATS, request.getStatsType().getKey()); - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromStoreReviewsRequest(StoreReviewsRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEWS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, storeApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addCommonPagingQueryParams(httpUrlBuilder, request.getLimit(), request.getOffset()); - - if (!request.getReviewIncludeTypes().isEmpty()) { - if (request.getReviewIncludeTypes().contains(ReviewIncludeType.PRODUCTS)) { - // "Stats=Reviews must be used in conjunction with Include=Products" - // - https://developer.bazaarvoice.com/conversations-api/reference/v5.4/reviews/review-display#requesting-all-reviews-for-a-particular-product-with-review-statistics-(inc.-average-rating) - httpUrlBuilder.addQueryParameter(KEY_STATS, STATS_REVIEWS); - } - String includeStr = StringUtils.componentsSeparatedBy(request.getReviewIncludeTypes(), ","); - httpUrlBuilder.addQueryParameter(kINCLUDE, includeStr); - } - - if (!request.getSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT, StringUtils.componentsSeparatedBy(request.getSorts(), ",")); - } - - // TODO: All of these repeated query params are begging for some better composability. Composite or Decorator pattern? - if (request.getSearchPhrase() != null) { - httpUrlBuilder.addQueryParameter(kSEARCH, request.getSearchPhrase()); - } - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromBulkProductRequest(BulkProductRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(PRODUCTS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addSortableProductParams(httpUrlBuilder, request); - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromProductDisplayPageRequest(ProductDisplayPageRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(PRODUCTS_ENDPOINT); - - addCommonQueryParams(httpUrlBuilder, convApiKey, bvMobileInfo); - addCommonDisplayQueryParams(httpUrlBuilder, request); - addSortableProductParams(httpUrlBuilder, request); - - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .build(); - } - - private Request createFromFeedbackSubmissionRequest(FeedbackSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(FEEDBACK_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo); - - formPutSafe(formBodyBuilder, kCONTENT_ID, request.getContentId()); - formPutSafe(formBodyBuilder, kCONTENT_TYPE, request.getContentType()); - formPutSafe(formBodyBuilder, kFEEDBACK_TYPE, request.getFeedbackType()); - formPutSafe(formBodyBuilder, kVOTE, request.getFeedbackVote()); - formPutSafe(formBodyBuilder, kREASON_TEXT, request.getReasonFlaggedText()); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - - private Request createFromReviewSubmissionRequest(ReviewSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEW_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo); - addCommonReviewSubmissionFormParams(formBodyBuilder, request); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - - private Request createFromStoreReviewSubmissionRequest(StoreReviewSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(REVIEW_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, storeApiKey, bvMobileInfo); - addCommonReviewSubmissionFormParams(formBodyBuilder, request); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - - private Request createFromQuestionSubmissionRequest(QuestionSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(QUESTION_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo); - formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId()); - formPutSafe(formBodyBuilder, kQUESTION_SUMMARY, request.getQuestionSummary()); - formPutSafe(formBodyBuilder, kQUESTION_DETAILS, request.getQuestionDetails()); - formPutSafe(formBodyBuilder, kIS_ANONUSER, request.getUserAnonymous()); - formPutSafe(formBodyBuilder, kSEND_EMAIL_ANSWERED, request.getSendEmailAlertWhenAnswered()); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - - private Request createFromAnswerSubmissionRequest(AnswerSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(ANSWER_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo); - formPutSafe(formBodyBuilder, kQUESTIONID, request.getQuestionId()); - formPutSafe(formBodyBuilder, kANSWERTEXT, request.getAnswerText()); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - - private Request createFromCommentSubmissionRequest(CommentSubmissionRequest request) { - Request.Builder okRequestBuilder = new Request.Builder(); - - HttpUrl.Builder httpUrlBuilder = HttpUrl.parse(bvRootApiUrl) - .newBuilder() - .addPathSegments(COMMENT_SUBMIT_ENDPOINT); - - FormBody.Builder formBodyBuilder = new FormBody.Builder(); - - addCommonSubmissionFormParams(formBodyBuilder, request, convApiKey, bvMobileInfo); - formPutSafe(formBodyBuilder, KEY_REVIEW_ID, request.getReviewId()); - formPutSafe(formBodyBuilder, KEY_COMMENT_TEXT, request.getCommentText()); - formPutSafe(formBodyBuilder, KEY_TITLE, request.getTitle()); - - RequestBody requestBody = formBodyBuilder.build(); - HttpUrl httpUrl = httpUrlBuilder.build(); - - Headers.Builder headersBuilder = new Headers.Builder(); - addCommonHeaders(headersBuilder, bvSdkUserAgent); - Headers headers = headersBuilder.build(); - - return okRequestBuilder - .url(httpUrl) - .headers(headers) - .post(requestBody) - .build(); - } - // endregion - - // region Static Helpers - private static void addCommonQueryParams(HttpUrl.Builder httpUrlBuilder, String apiKey, BVMobileInfo bvMobileInfo) { - httpUrlBuilder.addQueryParameter(kAPI_VERSION, API_VERSION) - .addQueryParameter(kPASS_KEY, apiKey) - .addQueryParameter(kAPP_ID, bvMobileInfo.getMobileAppIdentifier()) - .addQueryParameter(kAPP_VERSION, bvMobileInfo.getMobileAppVersion()) - .addQueryParameter(kBUILD_NUM, bvMobileInfo.getMobileAppCode()) - .addQueryParameter(kSDK_VERSION, bvMobileInfo.getBvSdkVersion()); - } - - private static void addCommonPagingQueryParams(HttpUrl.Builder httpUrlBuilder, int limit, int offset) { - httpUrlBuilder.addQueryParameter(kLIMIT, String.valueOf(limit)); - httpUrlBuilder.addQueryParameter(kOFFSET, String.valueOf(offset)); - } - - private static void addCommonDisplayQueryParams(HttpUrl.Builder httpUrlBuilder, ConversationsDisplayRequest request) { - addFilterQueryParams(httpUrlBuilder, request.getFilters()); - addExtraQueryParams(httpUrlBuilder, request.getExtraParams()); - } - - private static void addFilterQueryParams(HttpUrl.Builder httpUrlBuilder, List filters) { - for (Filter filter : filters) { - httpUrlBuilder - .addEncodedQueryParameter(kFILTER, filter.toString()); - } - } - - private static void addExtraQueryParams(HttpUrl.Builder httpUrlBuilder, List extraParams) { - for (ConversationsDisplayRequest.QueryPair queryPair : extraParams) { - if (queryPair.getKey() == null || queryPair.getValue() == null) { - continue; - } - httpUrlBuilder - .addQueryParameter(queryPair.getKey(), queryPair.getValue()); - } - } - - private static void addCommonHeaders(Headers.Builder headersBuilder, String bvSdkUserAgent) { - headersBuilder.add(KEY_USER_AGENT, bvSdkUserAgent); - } - - private static void addSortableProductParams(HttpUrl.Builder httpUrlBuilder, SortableProductRequest request) { - if (!request.getReviewSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_REVIEW, StringUtils.componentsSeparatedBy(request.getReviewSorts(), ",")); - } - - if (!request.getQuestionSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_QUESTIONS, StringUtils.componentsSeparatedBy(request.getQuestionSorts(), ",")); - } - - if (!request.getAnswerSorts().isEmpty()){ - httpUrlBuilder.addQueryParameter(kSORT_ANSWERS, StringUtils.componentsSeparatedBy(request.getAnswerSorts(), ",")); - } - - if (!request.getIncludes().isEmpty()) { - httpUrlBuilder.addQueryParameter(kINCLUDE, StringUtils.componentsSeparatedBy(request.getIncludes(), ",")); - } - - for (Include include : request.getIncludes()) { - httpUrlBuilder.addQueryParameter(include.getLimitParamKey(), String.valueOf(include.getLimit())); - } - - if (!request.getStatistics().isEmpty()) { - httpUrlBuilder.addQueryParameter(kSTATS, StringUtils.componentsSeparatedBy(request.getStatistics(), ",")); - } - } - - private static void formPutSafe(FormBody.Builder formBodyBuilder, @NonNull String key, @Nullable Object value) { - if (value != null) { - String valueStr = String.valueOf(value); - formBodyBuilder.add(key, valueStr); - } - } - - private static void addCommonSubmissionFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request, String apiKey, BVMobileInfo bvMobileInfo) { - formPutSafe(formBodyBuilder, kAPI_VERSION, API_VERSION); - formPutSafe(formBodyBuilder, kPASS_KEY, apiKey); - formPutSafe(formBodyBuilder, kAPP_ID, bvMobileInfo.getMobileAppIdentifier()); - formPutSafe(formBodyBuilder, kAPP_VERSION, bvMobileInfo.getMobileAppVersion()); - formPutSafe(formBodyBuilder, kBUILD_NUM, bvMobileInfo.getMobileAppCode()); - formPutSafe(formBodyBuilder, kSDK_VERSION, bvMobileInfo.getBvSdkVersion()); - formPutSafe(formBodyBuilder, kCAMPAIGN_ID, request.getCampaignId()); - formPutSafe(formBodyBuilder, kFINGER_PRINT, request.getFingerPrint()); - formPutSafe(formBodyBuilder, kHOSTED_AUTH_EMAIL, request.getHostedAuthenticationEmail()); - formPutSafe(formBodyBuilder, kHOST_AUTH_CALLBACK, request.getHostedAuthenticationCallback()); - formPutSafe(formBodyBuilder, kLOCALE, request.getLocale()); - formPutSafe(formBodyBuilder, kUSER, request.getUser()); - formPutSafe(formBodyBuilder, kUSER_EMAIL, request.getUserEmail()); - formPutSafe(formBodyBuilder, kUSER_ID, request.getUserId()); - formPutSafe(formBodyBuilder, kUSER_LOCATION, request.getUserLocation()); - formPutSafe(formBodyBuilder, kUSER_NICKNAME, request.getUserNickname()); - formPutSafe(formBodyBuilder, kSEND_EMAIL_PUBLISHED, request.getSendEmailAlertWhenPublished()); - formPutSafe(formBodyBuilder, kAGREE_TERMS, request.getAgreedToTermsAndConditions()); - formPutSafe(formBodyBuilder, kACTION, getAction(request).getKey()); - - addSubmissionPhotosFormParams(formBodyBuilder, request); - } - - private static void addSubmissionPhotosFormParams(FormBody.Builder formBodyBuilder, ConversationsSubmissionRequest request) { - final List photos = request.getPhotos(); - if (photos != null) { - int idx = 0; - for (Photo photo : photos) { - final String keyPhotoUrl = String.format(Locale.US, KEY_PHOTO_URL_TEMPLATE, idx); - final String keyPhotoCaption = String.format(Locale.US, KEY_PHOTO_CAPTION_TEMPLATE, idx); - formPutSafe(formBodyBuilder, keyPhotoUrl, photo.getContent().getNormalUrl()); - formPutSafe(formBodyBuilder, keyPhotoCaption, photo.getCaption()); - idx++; - } - } - } - - private static void addCommonReviewSubmissionFormParams(FormBody.Builder formBodyBuilder, BaseReviewSubmissionRequest request) { - formPutSafe(formBodyBuilder, kPRODUCT_ID, request.getProductId()); - formPutSafe(formBodyBuilder, kIS_RECOMMENDED, request.getRecommended()); - formPutSafe(formBodyBuilder, kSEND_EMAIL_COMMENTED, request.getSendEmailAlertWhenCommented()); - formPutSafe(formBodyBuilder, kRATING, request.getRating()); - formPutSafe(formBodyBuilder, kNET_PROMOTER_SCORE, request.getNetPromoterScore()); - formPutSafe(formBodyBuilder, kNET_PROMOTER_COMMENT, request.getNetPromoterComment()); - formPutSafe(formBodyBuilder, kTITLE, request.getTitle()); - formPutSafe(formBodyBuilder, kREVIEW_TEXT, request.getReviewText()); - - final List predefinedTags = request.getPredefinedTags(); - for (int i=0; i freeFormTags = request.getFreeFormTags(); - final List freeFormTagKeys = new ArrayList<>(freeFormTags.keySet()); - for (int i=0; i ratingSliderKeys = request.getRatingSliders().keySet(); - for (String key : ratingSliderKeys) { - final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key); - formPutSafe(formBodyBuilder, keyRating, request.getRatingSliders().get(key)); - } - - final Set ratingQuestionsKeys = request.getRatingQuestions().keySet(); - for (String key : ratingQuestionsKeys) { - final String keyRating = String.format(Locale.US, KEY_RATING_TEMPLATE, key); - formPutSafe(formBodyBuilder, keyRating, request.getRatingQuestions().get(key)); - } - - final Set contextDataValueKeys = request.getContextDataValues().keySet(); - for (String key : contextDataValueKeys) { - final String keyRating = String.format(Locale.US, KEY_CDV_TEMPLATE, key); - formPutSafe(formBodyBuilder, keyRating, request.getContextDataValues().get(key)); - } - - final Map additionalFields = request.getAdditionalFields(); - for (String key : additionalFields.keySet()) { - final String value = additionalFields.get(key); - final String keyAdditionalField = String.format(Locale.US, KEY_ADDITIONAL_PARAM_TEMPLATE, key); - formPutSafe(formBodyBuilder, keyAdditionalField, value); - } - - final List videoDataList = request.getVideoSubmissionData(); - for (int i=0; i Request create(RequestType request); } diff --git a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java index 632d0590..b2ea9d3a 100644 --- a/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java +++ b/bvconversations/src/main/java/com/bazaarvoice/bvandroidsdk/ReviewSubmissionRequest.java @@ -25,6 +25,12 @@ public class ReviewSubmissionRequest extends BaseReviewSubmissionRequest { super(builder); } + public ReviewSubmissionRequest.Builder newBuilder() { + ReviewSubmissionRequest.Builder builder = new ReviewSubmissionRequest.Builder(getAction(), getProductId()); + + return builder; + } + public static class Builder extends BaseReviewBuilder { public Builder(Action action, String productId) { super(action, productId); diff --git a/bvconversations/src/res/values/strings.xml b/bvconversations/src/res/values/strings.xml deleted file mode 100644 index a048f379..00000000 --- a/bvconversations/src/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - BVSDK Conversations - \ No newline at end of file diff --git a/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java b/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java similarity index 96% rename from bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java rename to bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java index c4808c94..f8a111a9 100644 --- a/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/RequestFactoryTest.java +++ b/bvconversations/src/test/java/com/bazaarvoice/bvandroidsdk/BasicRequestFactoryTest.java @@ -25,8 +25,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class RequestFactoryTest { - private RequestFactory requestFactory; +public class BasicRequestFactoryTest { + private BasicRequestFactory requestFactory; private GenericFormBodyParams genericFormBodyParams; @Before @@ -351,6 +351,12 @@ public void reviewSubmissionShouldHaveOwnFormParams() throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "VideoCaption_1", "Totes%20cool%20100%25%21"); } + @Test + public void reviewSubmissionShouldHaveFingerprintProvided() throws Exception { + final Request okRequest = createFullReviewSubmissionRequest(); + assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp"); + } + @Test public void submissionShouldNotAllowNullFormParams() throws Exception { final ReviewSubmissionRequest request = new ReviewSubmissionRequest.Builder(Action.Preview, "prod1") @@ -401,6 +407,12 @@ public void storeReviewSubmissionShouldHaveOwnFormParams() throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "VideoCaption_1", "Totes%20cool%20100%25%21"); } + @Test + public void storeReviewSubmissionShouldHaveFingerprintProvided() throws Exception { + final Request okRequest = createFullStoreReviewSubmissionRequest(); + assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp"); + } + @Test public void questionSubmissionShouldHaveBVSDKFormParams() throws Exception { final Request okRequest = createFullQuestionSubmissionRequest(); @@ -431,6 +443,12 @@ public void questionSubmissionShouldHaveOwnFormParams() throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "SendEmailAlertWhenAnswered", "true"); } + @Test + public void questionSubmissionShouldHaveFingerprintProvided() throws Exception { + final Request okRequest = createFullQuestionSubmissionRequest(); + assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp"); + } + @Test public void answerSubmissionShouldHaveBVSDKFormParams() throws Exception { final Request okRequest = createFullAnswerSubmissionRequest(); @@ -450,6 +468,12 @@ public void answerSubmissionShouldHaveOwnFormParams() throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "AnswerText", "Some%20great%20answer%20%24%25%20here"); } + @Test + public void answerSubmissionShouldHaveFingerprintProvided() throws Exception { + final Request okRequest = createFullAnswerSubmissionRequest(); + assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp"); + } + @Test public void commentSubmissionShouldHaveBVSDKFormParams() throws Exception { final Request okRequest = createFullCommentSubmissionRequest(); @@ -476,6 +500,12 @@ public void commentSubmissionShouldHaveOwnFormParams() throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "Title", "Something%20**great**%21"); } + @Test + public void commentSubmissionShouldHaveFingerprintProvided() throws Exception { + final Request okRequest = createFullCommentSubmissionRequest() ; + assertFormBodyContainsKeyValEncoded(okRequest, "fp", "test_fp"); + } + // region Helper Assertions private void assertFormBodyContainsKeyValEncoded(Request okRequest, String encodedKey, String encodedVal) throws Exception { @@ -577,7 +607,6 @@ private void assertContainsBVMobileInfoFormParams(Request okRequest) throws Exce private void assertContainsConversationsSubmissionRequestFormParams(Request okRequest) throws Exception { assertFormBodyContainsKeyValEncoded(okRequest, "UserId", genericFormBodyParams.getUserId()); assertFormBodyContainsKeyValEncoded(okRequest, "UserNickname", genericFormBodyParams.getUserNickname()); - assertFormBodyContainsKeyValEncoded(okRequest, "fp", genericFormBodyParams.getFingerprint()); assertFormBodyContainsKeyValEncoded(okRequest, "hostedauthentication_authenticationemail", "email%40email.com"); assertFormBodyContainsKeyValEncoded(okRequest, "hostedauthentication_callbackurl", "https%3A%2F%2Fwww.callback.url"); assertFormBodyContainsKeyValEncoded(okRequest, "campaignid", genericFormBodyParams.getCampaignId()); @@ -591,7 +620,7 @@ private void assertContainsConversationsSubmissionRequestFormParams(Request okRe // endregion // region Stub Data - private RequestFactory createTestRequestFactory() { + private BasicRequestFactory createTestRequestFactory() { final BVMobileInfo bvMobileInfo = mock(BVMobileInfo.class); when(bvMobileInfo.getBvSdkVersion()).thenReturn("bvsdk7"); when(bvMobileInfo.getMobileAppCode()).thenReturn("1"); @@ -612,7 +641,13 @@ private RequestFactory createTestRequestFactory() { .apiKeyConversationsStores(storeApiKey) .clientId("yoyomakers") .build(); - return new RequestFactory(bvMobileInfo, bvRootApiUrls, bvConfig, bvSdkUserAgent); + final FingerprintProvider fingerprintProvider = new FingerprintProvider() { + @Override + public String getFingerprint() { + return "test_fp"; + } + }; + return new BasicRequestFactory(bvMobileInfo, bvRootApiUrls, bvConfig, bvSdkUserAgent, fingerprintProvider); } private List createPhotoList(PhotoUpload.ContentType contentType) { diff --git a/bvcurations/src/main/res/values/strings.xml b/bvcurations/src/main/res/values/strings.xml deleted file mode 100644 index 79bae69f..00000000 --- a/bvcurations/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - BVSDK Curations - \ No newline at end of file diff --git a/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java b/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java index 55b3215a..30e9c3de 100644 --- a/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java +++ b/bvcurationsui/src/main/java/com/bazaarvoice/bvandroidsdk/CurationsAspectRatioImageView.java @@ -10,7 +10,7 @@ import static android.view.View.MeasureSpec.EXACTLY; -final class CurationsAspectRatioImageView extends AppCompatImageView { +public final class CurationsAspectRatioImageView extends AppCompatImageView { private static final String TAG = "AspRatioImageView"; private int widthRatio; private int heightRatio; diff --git a/bvcurationsui/src/main/res/values/curationsui_strings.xml b/bvcurationsui/src/main/res/values/curationsui_strings.xml deleted file mode 100644 index e49fb209..00000000 --- a/bvcurationsui/src/main/res/values/curationsui_strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvcurationsui - diff --git a/bvlocation/build.gradle b/bvlocation/build.gradle index 59aa9082..347dafda 100644 --- a/bvlocation/build.gradle +++ b/bvlocation/build.gradle @@ -14,6 +14,7 @@ dependencies { compile sdkDep.gimbalSLF4J compile sdkDep.slf4jApi compile sdkDep.appCompatV7 + provided sdkDep.gson // Dependencies for local unit tests testCompile sdkTestDep.junit diff --git a/bvlocation/src/main/res/values/strings.xml b/bvlocation/src/main/res/values/strings.xml deleted file mode 100644 index 9f7ee18f..00000000 --- a/bvlocation/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - BVSDK Location - diff --git a/bvnotifications/src/main/res/values/strings.xml b/bvnotifications/src/main/res/values/strings.xml deleted file mode 100644 index ab9c2442..00000000 --- a/bvnotifications/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvandroidsdknotifications - diff --git a/bvpin/src/main/res/values/strings.xml b/bvpin/src/main/res/values/strings.xml deleted file mode 100644 index adbddfce..00000000 --- a/bvpin/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvandroidsdkpin - diff --git a/bvpinnotifications/src/main/res/values/strings.xml b/bvpinnotifications/src/main/res/values/strings.xml deleted file mode 100644 index 149de739..00000000 --- a/bvpinnotifications/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvpinnotifications - diff --git a/bvrecommendations/build.gradle b/bvrecommendations/build.gradle index 33b4529a..fe7f836a 100644 --- a/bvrecommendations/build.gradle +++ b/bvrecommendations/build.gradle @@ -12,6 +12,7 @@ dependencies { compile project(':bvcommon') provided sdkDep.recyclerview provided sdkDep.gson + provided sdkDep.okhttp3 // Dependencies for local unit tests testCompile sdkTestDep.junit diff --git a/bvrecommendations/src/main/res/values/strings.xml b/bvrecommendations/src/main/res/values/strings.xml deleted file mode 100644 index 46807bae..00000000 --- a/bvrecommendations/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - BVSDK Recommendations - \ No newline at end of file diff --git a/bvstorenotifications/build.gradle b/bvstorenotifications/build.gradle index bb5a2b2a..8e54d4fe 100644 --- a/bvstorenotifications/build.gradle +++ b/bvstorenotifications/build.gradle @@ -16,6 +16,7 @@ dependencies { compile sdkDep.gimbalSLF4J compile sdkDep.slf4jApi compile sdkDep.appCompatV7 + provided sdkDep.gson // Dependencies for local unit tests testCompile sdkTestDep.junit diff --git a/bvstorenotifications/src/main/res/values/strings.xml b/bvstorenotifications/src/main/res/values/strings.xml deleted file mode 100644 index 6326c352..00000000 --- a/bvstorenotifications/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - bvstorenotifications - diff --git a/dependencies.gradle b/dependencies.gradle index 946069b1..8c39af81 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -16,14 +16,14 @@ ext { // the version number in one location for those artifacts sdkSharedVersions = [ okhttp3Version : '3.2.0', - supportLibraryVersion : '25.0.0', + supportLibraryVersion : '25.2.0', gimbalVersion : '2.46', - playServicesVersion : '9.0.0', + playServicesVersion : '11.0.0', robolectric: '3.2.2' ] sdkBuildDep = [ - androidPlugin : 'com.android.tools.build:gradle:2.3.2', + androidPlugin : 'com.android.tools.build:gradle:2.3.3', credentialsPlugin : 'nu.studer:gradle-credentials-plugin:1.0.1', playServicesPlugin : 'com.google.gms:google-services:3.0.0', jacocoPlugin : 'com.dicedmelon.gradle:jacoco-android:0.1.1' diff --git a/gradle.properties b/gradle.properties index 8f0665c0..392ad5a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=6.8.1 +VERSION_NAME=6.9.0-RC2-BV-SNAPSHOT GROUP=com.bazaarvoice.bvandroidsdk POM_DESCRIPTION=Bazaarvoice Android SDK diff --git a/settings.gradle b/settings.gradle index 26da5b21..c431360a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ include ':app', + ':bvauthiovation', ':bvanalytics', ':bvcommon', ':bvrecommendations', @@ -10,4 +11,4 @@ include ':app', ':bvnotifications', ':bvstorenotifications', ':bvpinnotifications', - ':bvcurationsui' + ':bvcurationsui' \ No newline at end of file