diff --git a/BridgeAPI.md b/BridgeAPI.md
index 0d955de..b8f7937 100644
--- a/BridgeAPI.md
+++ b/BridgeAPI.md
@@ -28,6 +28,9 @@ native modules for both platforms: Android and iOS.
- getLookupData
- getExperiences
- experienceShown
+- getPlacement
+- placementImpression
+- placementClickthrough
-------------------------------------------------------
## **init(trackingId, logLevel)**
@@ -293,3 +296,109 @@ None
### Example
QubitSDK.experienceShown("https://sse.qubit.com/v1/callback?data=igK....n0=");
+
+-------------------------------------------------------
+
+## **getPlacement**(placementId, mode, attributes, campaignId, experienceId, placementPromise)
+
+### Description
+Returns Placement for given parameters.
+
+### Parameters
+- placementId
+ - Type: String
+ - Constraints: Not null
+ - Description: Unique ID of the placement.
+- mode
+ - Type: String
+ - Constraints: Can be one of LIVE/SAMPLE/PREVIEW.
+ - Description: The mode to fetch placements content with. Defaults to LIVE.
+- attributes
+ - Type: String
+ - Constraints: Should be string description of JSON or null
+ - Description: JSON string containing custom attributes to be used to query for the placement. "visitor" attribute will be ignored as it is set by SDK.
+- campaignId
+ - Type: String
+ - Constraints: Nullable
+ - Description: Campaign identifier
+- experienceId
+ - Type: String
+ - Constraints: Nullable
+ - Description: Experience identifier
+
+### Result
+Promise with a map describing Placement object:
+
+ {
+ "content": { ... }
+ "impressionUrl": "https://api.qubit.com/placements/callback?data=ggW4eyJtZXRhIjp7ImlkIjo",
+ "clickthroughUrl": "https://api.qubit.com/placements/callback?data=mQW4eyJtZXRhIjp7Imlkx"
+ }
+The structure of response content depends on the type of placement that is being called.
+
+### Exceptions
+- Exception is thrown, when SDK is not initialized.
+
+### Example
+ async () => {
+ const placement = await QubitSDK.getPlacement(
+ "placement_id",
+ "LIVE",
+ "{ \"color\": \"blue\"}",
+ "campaign_id",
+ "experience_id"
+ );
+ ...
+ }
+
+-------------------------------------------------------
+
+## placementImpression(callbackUrl)
+
+### Description
+Sends request to URL described by placement impression callback.
+
+### Parameters
+- callbackUrl
+ - Type: String
+ - Constraints: Not null
+ - Description: Impression callback URL.
+
+
+### Result
+None
+
+### Example
+ async () => {
+ const placement = await QubitSDK.placementImpression(
+ "https://some.url.com"
+ );
+ ...
+ }
+
+-------------------------------------------------------
+
+## placementClickthrough(callbackUrl)
+
+### Description
+Sends request to URL described by placement clickthrough callback.
+
+### Parameters
+- callbackUrl
+ - Type: String
+ - Constraints: Not null
+ - Description: Clickthrough callback URL.
+
+
+### Result
+None
+
+### Example
+ async () => {
+ const placement = await QubitSDK.placementClickthrough(
+ "https://some.url.com"
+ );
+ ...
+ }
+
+
diff --git a/README.md b/README.md
index fe7f2ae..1d72e34 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,11 @@ Installation of the QubitSDK, to provide event tracking and lookup. To make use
### Installation
-1. `$ npm install qubit-sdk-react-native --save`
-or
-`$ yarn add qubit-sdk-react-native`
+1. `$ npm install qubit-sdk-react-native --save`
+ or
+ `$ yarn add qubit-sdk-react-native`
-2. Navigate to your `/ios` directory and run `pod install` to ensure the `QubitSDK` CocoaPod is installed. Android should require no further installation.
+2. Navigate to your `/ios` directory and run `pod install` to ensure the `QubitSDK` CocoaPod is installed. Android should require no further installation.
Optional - if you are using React Native < 0.60, you must `link` the library.
@@ -54,6 +54,9 @@ and send first event
- [getExperiences](#getexperiences)
- [Parameters](#parameters-3)
- [Examples](#examples-6)
+- [getPlacement](#getplacement)
+ - [Parameters](#parameters-4)
+ - [Examples](#examples-7)
#### start
@@ -214,6 +217,42 @@ async () => {
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<Experience>>** Promise with an array of Experience objects.
+#### getPlacement
+
+Returns Placement for given parameters.
+
+##### Parameters
+
+- `placementId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Unique ID of the placement.
+- `mode` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** The mode to fetch placements content with, can be one of LIVE/SAMPLE/PREVIEW. Defaults to LIVE.
+- `attributes` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** JSON string containing custom attributes to be used to query for the placement. "visitor" attribute will be ignored as it is set by SDK.
+- `campaignId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Optional.
+- `experienceId` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** Optional.
+
+##### Examples
+
+```javascript
+async () => {
+ const placement = await getPlacement(
+ "placement_id",
+ "LIVE",
+ "{ \"color\": \"blue\"}",
+ "campaign_id",
+ "experience_id"
+ );
+ ...
+ placement.impression();
+ ...
+ placement.clickthrough();
+}
+
+{
+ "content": { ... }
+}
+```
+
+Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<Placement>** Promise with an object describing Placement object.
+
### Compatibility
Qubit SDK React Native is compatible with React Native 0.58 and higher
diff --git a/android/build.gradle b/android/build.gradle
index c60967d..9bab2ba 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -58,6 +58,6 @@ dependencies {
implementation "androidx.annotation:annotation:1.0.0"
implementation "com.google.code.gson:gson:2.8.2"
- implementation 'com.qubit:qubit-sdk-android:1.4.1'
+ implementation 'com.qubit:qubit-sdk-android:2.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.0"
}
diff --git a/android/src/main/java/com/qubit/reactnative/sdk/QubitSDKModule.java b/android/src/main/java/com/qubit/reactnative/sdk/QubitSDKModule.java
index 7cca174..a94c632 100644
--- a/android/src/main/java/com/qubit/reactnative/sdk/QubitSDKModule.java
+++ b/android/src/main/java/com/qubit/reactnative/sdk/QubitSDKModule.java
@@ -1,7 +1,7 @@
package com.qubit.reactnative.sdk;
import android.util.Log;
-import androidx.annotation.NonNull;
+
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
@@ -12,18 +12,24 @@
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import com.qubit.android.sdk.api.QubitSDK;
import com.qubit.android.sdk.api.logging.QBLogLevel;
+import com.qubit.android.sdk.api.placement.PlacementMode;
+import com.qubit.android.sdk.api.placement.PlacementPreviewOptions;
import com.qubit.android.sdk.api.tracker.event.QBEvent;
import com.qubit.android.sdk.api.tracker.event.QBEvents;
import com.qubit.android.sdk.internal.experience.Experience;
-import com.qubit.android.sdk.internal.experience.callback.CallbackConnector;
-import com.qubit.android.sdk.internal.experience.callback.CallbackConnectorImpl;
+import com.qubit.android.sdk.internal.experience.callback.ExperienceCallbackConnector;
+import com.qubit.android.sdk.internal.experience.callback.ExperienceCallbackConnectorImpl;
import com.qubit.android.sdk.internal.experience.model.ExperiencePayload;
import com.qubit.android.sdk.internal.lookup.LookupData;
+
import java.util.ArrayList;
import java.util.List;
+import androidx.annotation.NonNull;
+
public class QubitSDKModule extends ReactContextBaseJavaModule {
private static ReactApplicationContext reactContext;
@@ -139,10 +145,68 @@ public void getExperiences(ReadableArray experienceIds,
@ReactMethod
public void experienceShown(String callback) {
- CallbackConnector callbackConnector = new CallbackConnectorImpl(callback, QubitSDK.getDeviceId());
+ ExperienceCallbackConnector callbackConnector = new ExperienceCallbackConnectorImpl(callback, QubitSDK.getDeviceId());
callbackConnector.shown();
}
+ @ReactMethod
+ public void getPlacement(
+ String placementId,
+ String mode,
+ String attributes,
+ String campaignId,
+ String experienceId,
+ Promise placementPromise
+ ) {
+ QubitSDK.getPlacement(
+ placementId,
+ matchMode(mode),
+ getAttributesJson(attributes),
+ new PlacementPreviewOptions(campaignId, experienceId),
+ placement -> {
+ JsonObject placementJson = new JsonObject();
+ placementJson.add("content", placement.getContent());
+ placementJson.addProperty("impressionUrl", placement.getImpressionUrl());
+ placementJson.addProperty("clickthroughUrl", placement.getClickthroughUrl());
+ placementPromise.resolve(WritableMapConverter.convertJsonToMap(placementJson));
+ return null;
+ },
+ throwable -> {
+ placementPromise.reject(throwable);
+ return null;
+ }
+ );
+ }
+
+ @ReactMethod
+ public void placementImpression(String callbackUrl) {
+ QubitSDK.sendCallbackRequest(callbackUrl);
+ }
+
+ @ReactMethod
+ public void placementClickthrough(String callbackUrl) {
+ QubitSDK.sendCallbackRequest(callbackUrl);
+ }
+
+ private PlacementMode matchMode(String value) {
+ switch (value) {
+ case "SAMPLE":
+ return PlacementMode.SAMPLE;
+ case "PREVIEW":
+ return PlacementMode.PREVIEW;
+ case "LIVE":
+ default:
+ return PlacementMode.LIVE;
+ }
+ }
+
+ private JsonObject getAttributesJson(String attributes) {
+ try {
+ return new JsonParser().parse(attributes).getAsJsonObject();
+ } catch (Exception e) {
+ return null;
+ }
+ }
private static QBLogLevel defaultLogLevel = QBLogLevel.WARN;
@@ -150,7 +214,7 @@ private QBLogLevel parseLogLevel(String logLevel) {
if (logLevel == null || logLevel.isEmpty()) {
return defaultLogLevel;
}
- for(QBLogLevel level : QBLogLevel.values()) {
+ for (QBLogLevel level : QBLogLevel.values()) {
if (level.toString().equalsIgnoreCase(logLevel))
return level;
}
diff --git a/example/App.js b/example/App.js
index eb1a741..f02dabd 100644
--- a/example/App.js
+++ b/example/App.js
@@ -42,6 +42,39 @@ class App extends PureComponent {
exp.forEach(e => console.log(e));
};
+ getPlacement = async () => {
+ const placement = await QubitSDK.getPlacement(
+ "tsOujouCSSKJGSCMUsmQRw",
+ "LIVE",
+ null,
+ "1ybrhki9RvKWpA-9veLQSg",
+ null
+ );
+ console.log(placement);
+ };
+
+ sendPlacementImpression = async () => {
+ const placement = await QubitSDK.getPlacement(
+ "tsOujouCSSKJGSCMUsmQRw",
+ "LIVE",
+ null,
+ "1ybrhki9RvKWpA-9veLQSg",
+ null
+ );
+ placement.impression();
+ };
+
+ sendPlacementClickthrough = async () => {
+ const placement = await QubitSDK.getPlacement(
+ "tsOujouCSSKJGSCMUsmQRw",
+ "LIVE",
+ null,
+ "1ybrhki9RvKWpA-9veLQSg",
+ null
+ );
+ placement.clickthrough();
+ };
+
render() {
return (
<>
@@ -82,6 +115,21 @@ class App extends PureComponent {
Get experiences
+
+
+ Get placement
+
+
+
+
+ Send placement impression callback
+
+
+
+
+ Send placement clickthrough callback
+
+
>
)
diff --git a/ios/QubitSDKModule/QubitSDKModule.m b/ios/QubitSDKModule/QubitSDKModule.m
index 392c7ed..e2995a0 100644
--- a/ios/QubitSDKModule/QubitSDKModule.m
+++ b/ios/QubitSDKModule/QubitSDKModule.m
@@ -28,5 +28,15 @@ @interface RCT_EXTERN_REMAP_MODULE(QubitSDK, QubitSDKModule, NSObject)
resolver:(RCTPromiseResolveBlock) resolver
rejecter:(RCTPromiseRejectBlock) rejecter)
RCT_EXTERN_METHOD(experienceShown:(NSString *) callback)
+RCT_EXTERN_METHOD(getPlacement:
+ (NSString *) placementId
+ mode:(NSString *) mode
+ attributes:(NSString *) attributes
+ campaignId:(NSString *) campaignId
+ experienceId:(NSString *) experienceId
+ resolver:(RCTPromiseResolveBlock) resolver
+ rejecter:(RCTPromiseRejectBlock) rejecter)
+RCT_EXTERN_METHOD(placementImpression:(NSString *) callback)
+RCT_EXTERN_METHOD(placementClickthrough:(NSString *) callback)
@end
diff --git a/ios/QubitSDKModule/QubitSDKModule.swift b/ios/QubitSDKModule/QubitSDKModule.swift
index 5fc9747..e6a597f 100644
--- a/ios/QubitSDKModule/QubitSDKModule.swift
+++ b/ios/QubitSDKModule/QubitSDKModule.swift
@@ -61,6 +61,30 @@ class QubitSDKModule: NSObject {
func experienceShown(callback: String) {
QBExperienceEntityCallback(callback: callback).shown()
}
+
+ @objc(getPlacement:mode:attributes:campaignId:experienceId:resolver:rejecter:)
+ func getPlacement(placementId: String, mode: String, attributes: String, campaignId: String, experienceId: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
+ QubitSDK.getPlacement(with: placementId,
+ mode: mode,
+ attributes: attributes,
+ campaignId: campaignId,
+ experienceId: experienceId,
+ onSuccess: { result in
+ resolver( result.asDictionary )
+ }, onError: { error in
+ rejecter("Error", "QubitSDKModule: getPlacement failed.", error)
+ })
+ }
+
+ @objc(placementImpression:)
+ func placementImpression(callback: String) {
+ QBPlacementEntityCallback(impressionUrl: callback).impression()
+ }
+
+ @objc(placementClickthrough:)
+ func placementClickthrough(callback: String) {
+ QBPlacementEntityCallback(clickthroughUrl: callback).clickthrough()
+ }
@objc
static func requiresMainQueueSetup() -> Bool {
diff --git a/package.json b/package.json
index 3024298..415c55f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "qubit-sdk-react-native",
- "version": "1.0.7",
+ "version": "2.0.0",
"description": "React Native bridge for using native Qubit SDK libraries on iOS and Android",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
diff --git a/qubit-sdk-react-native.podspec b/qubit-sdk-react-native.podspec
index c2a0c66..b24cbe8 100644
--- a/qubit-sdk-react-native.podspec
+++ b/qubit-sdk-react-native.podspec
@@ -20,6 +20,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.dependency "React"
- s.dependency "QubitSDK"
- s.swift_versions = ['4.0', '4.1', '4.2', '5.0']
+ s.dependency "QubitSDK", "~> 2.0.0"
+ s.swift_versions = ['4.0', '4.1', '4.2', '5.0', '5.1', '5.2', '5.3']
end
diff --git a/src/index.ts b/src/index.ts
index 671af27..45f1ff7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -16,10 +16,16 @@ type Experience = {
isControl: boolean,
id: number,
callback: string,
- variation: number
+ variation: number,
shown: () => void
}
+type Placement = {
+ content: object,
+ impression: () => void,
+ clickthrough: () => void
+}
+
class QubitSDK {
/**
* Initialization of SDK. It should be called as early as possible after application start, only once and before any other interaction with the API.
@@ -183,7 +189,59 @@ class QubitSDK {
!(ignoreSegments == null),
ignoreSegments || false
)
- .then(experiences => experiences.map(e => ({...e, shown: () => { NativeModules.QubitSDK.experienceShown(e.callback || '')} })))
+ .then(experiences => experiences.map(e => ({
+ ...e,
+ shown: () => { NativeModules.QubitSDK.experienceShown(e.callback || '') }
+ })))
+ }
+
+ /**
+ * Returns Placement for given parameters.
+ * @param {string} placementId Unique ID of the placement.
+ * @param {string} [mode] The mode to fetch placements content with, can be one of LIVE/SAMPLE/PREVIEW. Defaults to LIVE.
+ * @param {string} [attributes] JSON string containing custom attributes to be used to query for the placement. "visitor" attribute will be ignored as it is set by SDK.
+ * @param {string} [campaignId] Optional.
+ * @param {string} [experienceId] Optional.
+ * @returns {Promise} Promise with an object describing Placement object.
+ * @example
+ *
+ * async () => {
+ * const placement = await getPlacement(
+ * "placement_id",
+ * "LIVE",
+ * "{ \"color\": \"blue\"}",
+ * "campaign_id",
+ * "experience_id"
+ * );
+ * ...
+ * placement.impression();
+ * ...
+ * placement.clickthrough();
+ * }
+ *
+ * {
+ * "content": { ... }
+ * }
+ */
+ public getPlacement(
+ placementId: string,
+ mode?: string,
+ attributes?: string,
+ campaignId?: string,
+ experienceId?: string
+ ) : Promise {
+ return NativeModules.QubitSDK.getPlacement(
+ placementId,
+ mode,
+ attributes,
+ campaignId,
+ experienceId
+ )
+ .then(placement => ({
+ content: placement.content,
+ impression: () => { NativeModules.QubitSDK.placementImpression(placement.impressionUrl || '') },
+ clickthrough: () => { NativeModules.QubitSDK.placementClickthrough(placement.clickthroughUrl || '') }
+ }))
}
}