Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Extends TargetRequests with content and (meta)data callback #295

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions packages/target/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,48 @@ var parameters = new TargetParameters(
Target.retrieveLocationContent(locationRequests, parameters);
```

### Load Target requests with metadata:

**Syntax**

```typescript
retrieveLocationContent(Array<TargetRequestObject>, <TargetParameters>): void
```

**Example**

```typescript
var mboxParameters = { status: "platinum" };
var purchaseIDs = ["34", "125"];

var targetOrder = new TargetOrder("ADCKKIM", 344.3, purchaseIDs);
var targetProduct = new TargetProduct("24D3412", "Books");
var parameters = new TargetParameters(mboxParameters, null, null, null);
var requestWithData = new TargetRequestObjectWithData(
"mboxNameWithData",
parameters,
"defaultContent",
(error, content, data) => {
if (error) {
console.error(error);
} else {
console.log("Adobe content and data:", content, data);
}
}
);

var locationRequests = [requestWithData];
var profileParameters = { ageGroup: "20-32" };

var parameters = new TargetParameters(
{ parameters: "parametervalue" },
profileParameters,
targetProduct,
targetOrder
);
Target.retrieveLocationContent(locationRequests, parameters);
```

### Using the prefetch APIs:

**Syntax**
Expand Down
85 changes: 80 additions & 5 deletions packages/target/__tests__/TargetTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { NativeModules } from 'react-native';
import Target from '../ts/Target';
import TargetPrefetchObject from '../ts/models/TargetPrefetchObject';
import TargetRequestObject from '../ts/models/TargetRequestObject';
import TargetRequestObjectWithData, {
TargetDataCallback,
} from '../ts/models/TargetRequestObjectWithData';
import TargetOrder from '../ts/models/TargetOrder';
import TargetProduct from '../ts/models/TargetProduct';
import TargetParameters from '../ts/models/TargetParameters';
Expand Down Expand Up @@ -133,6 +136,80 @@ describe('Target', () => {
expect(spy).toHaveBeenCalledWith(locationRequests, parameters);
});

test('retrieveLocationContent is called with correct parameters and metadata', async () => {
const spy = jest.spyOn(NativeModules.AEPTarget, 'retrieveLocationContent');

const metadata = {
responseTokens: {
'activity.id': '42',
'activity.name': 'Adobe test',
'experience.id': '0',
'experience.name': 'Adobe test',
},
};

NativeModules.AEPTarget.registerTargetRequestsWithData = jest.fn(
(_, callback: TargetDataCallback) => {
console.log('registering callback');
callback(null, 'content', metadata);
}
);

var mboxParameters1 = { status: 'platinum' };
var purchaseIDs = ['34', '125'];

var targetOrder = new TargetOrder('ADCKKIM', 344.3, purchaseIDs);
var targetProduct = new TargetProduct('24D3412', 'Books');
var parameters1 = new TargetParameters(mboxParameters1, null, null, null);
var request1 = new TargetRequestObjectWithData(
'mboxName2',
parameters1,
'defaultContent1',
(error, content, data) => {
if (error) {
console.error(error);
} else {
console.log('Adobe content and metadata:', content, data);
}
}
);

var parameters2 = new TargetParameters(
mboxParameters1,
{ profileParameters: 'parameterValue' },
targetProduct,
targetOrder
);
var request2 = new TargetRequestObjectWithData(
'mboxName2',
parameters2,
'defaultContent2',
(error, content, data) => {
if (error) {
console.error(error);
} else {
console.log('Adobe content and metadata:', content, data);
}
}
);

var locationRequests = [request1, request2];
var profileParameters1 = { ageGroup: '20-32' };

var parameters = new TargetParameters(
{ parameters: 'parametervalue' },
profileParameters1,
targetProduct,
targetOrder
);
await Target.retrieveLocationContent(locationRequests, parameters);

expect(spy).toHaveBeenCalledWith(locationRequests, parameters);
expect(
NativeModules.AEPTarget.registerTargetRequestsWithData
).toHaveBeenCalled();
});

test('displayedLocations is called with correct parameters', async () => {
const spy = jest.spyOn(NativeModules.AEPTarget, 'displayedLocations');
var purchaseIDs = ['34', '125'];
Expand Down Expand Up @@ -176,8 +253,7 @@ describe('Target', () => {
expect(spy).toHaveBeenCalledWith('locationName', parameters);
});

test('prefetchContent is called with correct parameters', async () => {

test('prefetchContent is called with correct parameters', async () => {
const spy = jest.spyOn(NativeModules.AEPTarget, 'prefetchContent');
var mboxParameters1 = { status: 'platinum' };
var purchaseIDs = ['34', '125'];
Expand Down Expand Up @@ -205,11 +281,10 @@ describe('Target', () => {
targetOrder
);


await Target.prefetchContent(prefetchList, parameters)
await Target.prefetchContent(prefetchList, parameters)
.then((success) => console.log(success))
.catch((err) => console.log(err));

expect(spy).toHaveBeenCalledWith(prefetchList, parameters);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

package com.adobe.marketing.mobile.reactnative.target;

import android.util.Log;

import com.adobe.marketing.mobile.AdobeCallback;
import com.adobe.marketing.mobile.AdobeError;
import com.adobe.marketing.mobile.target.AdobeTargetDetailedCallback;
import com.adobe.marketing.mobile.target.TargetOrder;
import com.adobe.marketing.mobile.target.TargetParameters;
import com.adobe.marketing.mobile.target.TargetPrefetch;
Expand All @@ -20,7 +24,9 @@
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;

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

Expand Down Expand Up @@ -65,6 +71,34 @@ public void call(String content) {
});
}

public static TargetRequest mapToRequestWithData(ReadableMap map, final Callback successCallback) {
if (map == null) {
return null;
}

TargetParameters parameters = mapToParameters(getNullableMap(map, TARGET_PARAMETERS_KEY));
return new TargetRequest(getNullableString(map, NAME_KEY), parameters, getNullableString(map, DEFAULT_CONTENT_KEY), new AdobeTargetDetailedCallback() {

@Override
public void call(String content, Map<String, Object> data) {
try {
// wrapped data in HashMap to ensure it is mutable
WritableMap metadata = data != null ? RCTAEPTargetMapUtil.toWritableMap(new HashMap<>(data)) : null;

successCallback.invoke(null, content, metadata);
}
catch (Exception exception) {
Log.e("RCTAEPTargetDataBridge", "Could not parse data", exception);
}
}

@Override
public void fail(AdobeError adobeError) {
successCallback.invoke(adobeError, null, null);
}
});
}

public static TargetParameters mapToParameters(ReadableMap map) {
if (map == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public static WritableMap toWritableMap(Map<String, Object> map) {
} else if (value instanceof String) {
writableMap.putString((String) pair.getKey(), (String) value);
} else if (value instanceof Map) {
writableMap.putMap((String) pair.getKey(), RCTAEPTargetMapUtil.toWritableMap((Map<String, Object>) value));
// wrapped value in HashMap to ensure it is mutable
writableMap.putMap((String) pair.getKey(), RCTAEPTargetMapUtil.toWritableMap((new HashMap<>((Map<String, Object>) value))));
} else if (value.getClass() != null && value.getClass().isArray()) {
writableMap.putArray((String) pair.getKey(), RCTAEPTargetArrayUtil.toWritableArray((Object[]) value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,10 @@ public void registerTargetRequests(ReadableMap requestMap, Callback successCallb
registeredTargetRequests.put(requestMap.getString(REQUEST_ID_KEY), request);
}

@ReactMethod
public void registerTargetRequestsWithData(ReadableMap requestMap, Callback successCallback) {
TargetRequest request = RCTAEPTargetDataBridge.mapToRequestWithData(requestMap, successCallback);
registeredTargetRequests.put(requestMap.getString(REQUEST_ID_KEY), request);
}

}
6 changes: 6 additions & 0 deletions packages/target/ios/src/AEPTargetRequestObjectDataBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
@import AEPTarget;
#import "AEPTargetRequestObjectDataBridge.h"

typedef void (^TargetRequestCallbackWithData)(NSString * _Nullable, NSDictionary<NSString *,id> * _Nullable);

@interface AEPTargetRequestObject (RCTBridge)

+ (AEPTargetRequestObject *)
targetRequestObjectFromDict:(NSDictionary *)dict
callback:(nullable void (^)(
NSString *__nullable content))callback;

+ (AEPTargetRequestObject *)
targetRequestObjectWithDataFromDict:(NSDictionary *)dict
contentWithDataCallback:(TargetRequestCallbackWithData)contentWithDataCallback;

@end
19 changes: 19 additions & 0 deletions packages/target/ios/src/AEPTargetRequestObjectDataBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,23 @@ @implementation AEPTargetRequestObject (RCTBridge)
initWithMboxName:dict[REQUEST_NAME_KEY] defaultContent:dict[DEFAULT_CONTENT_KEY] targetParameters:parameters contentCallback:callback];
}

+ (AEPTargetRequestObject *)
targetRequestObjectWithDataFromDict:(NSDictionary *)dict
contentWithDataCallback: (TargetRequestCallbackWithData) contentWithDataCallback {

if (!dict || [dict isEqual:[NSNull null]]) {
return nil;
}

AEPTargetParameters *parameters = [AEPTargetParameters
targetParametersFromDict:dict[REQUEST_PARAMETERS_KEY]];

return [[AEPTargetRequestObject alloc]
initWithMboxName:dict[REQUEST_NAME_KEY]
defaultContent:dict[DEFAULT_CONTENT_KEY]
targetParameters:parameters
contentWithDataCallback: contentWithDataCallback
];
}

@end
20 changes: 20 additions & 0 deletions packages/target/ios/src/RCTAEPTarget.m
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,24 @@ - (dispatch_queue_t)methodQueue {
_registeredTargetRequests[requestDict[@"id"]] = obj;
}

RCT_EXPORT_METHOD(registerTargetRequestsWithData
: (nonnull NSDictionary *)requestDict callback
: (RCTResponseSenderBlock)callback) {

TargetRequestCallbackWithData contentWithDataCallback = ^void(NSString * _Nullable content, NSDictionary<NSString *,id> * _Nullable data) {
NSDictionary *test = [[NSDictionary alloc] initWithDictionary: data];
callback(@[ [NSNull null], content, test]);
};

AEPTargetRequestObject *obj = [AEPTargetRequestObject
targetRequestObjectWithDataFromDict:requestDict
contentWithDataCallback: contentWithDataCallback];

if (!_registeredTargetRequests) {
_registeredTargetRequests = [NSMutableDictionary dictionary];
}

_registeredTargetRequests[requestDict[@"id"]] = obj;
}

@end
2 changes: 2 additions & 0 deletions packages/target/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import TargetParameters from './models/TargetParameters';
import TargetPrefetchObject from './models/TargetPrefetchObject';
import TargetProduct from './models/TargetProduct';
import TargetRequestObject from './models/TargetRequestObject';
import TargetRequestObjectWithData from './models/TargetRequestObjectWithData';

export {
// Native models
Expand All @@ -23,6 +24,7 @@ export {
TargetPrefetchObject,
TargetProduct,
TargetRequestObject,
TargetRequestObjectWithData,
// Native modules
Target
};
50 changes: 50 additions & 0 deletions packages/target/ts/models/TargetRequestObjectWithData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2022 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/

import TargetPrefetchObject from './TargetPrefetchObject';
import TargetParameters from './TargetParameters';
import { NativeModules } from 'react-native';

export type TargetDataCallback = (
error: Error | null,
content: string | null,
data: Record<string, any>
) => void;

interface ITargetRequests {
registerTargetRequestsWithData: (
requestMap: TargetRequestObjectWithData,
callback: TargetDataCallback
) => void;
}

const RCTTarget: ITargetRequests = NativeModules.AEPTarget;

class TargetRequestObjectWithData extends TargetPrefetchObject {
defaultContent: string;
id: string;

constructor(
name: string,
targetParameters: TargetParameters,
defaultContent: string,
callback: TargetDataCallback
) {
super(name, targetParameters);

this.defaultContent = defaultContent;
this.id = "_" + Math.random().toString(36).substr(2, 9);

RCTTarget.registerTargetRequestsWithData(this, callback);
}
}

export default TargetRequestObjectWithData;
3 changes: 2 additions & 1 deletion tests/jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ jest.doMock('react-native', () => {
prefetchContent: jest.fn(() => new Promise(resolve => resolve(''))),
displayedLocations: jest.fn(),
clickedLocation: jest.fn(),
registerTargetRequests: jest.fn()
registerTargetRequests: jest.fn(),
registerTargetRequestsWithData: jest.fn()
},
AEPPlaces: {
extensionVersion: jest.fn(() => new Promise(resolve => resolve(''))),
Expand Down