Rx Observable onErrorReturnItem - rx-java2

I have following code
public Observable<Map<Integer, String>> getMultipleCitiesName(List<Integer> cityIds) {
Observable<Map<Integer, String>> observable = Observable.create(s -> {
try {
System.out.println("getMultipleCitiesName ==="+Thread.currentThread().getName());
List<String> cityIdsString = new ArrayList<>();
for (Integer cityId : cityIds) {
cityIdsString.add(cityId.toString());
}
MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
formParams.put("cityIds[]", cityIdsString);
// Call the Location Client to call the API
Response<Map<Integer, String>> response = locationClient.getMultipleCitiesName(formParams);
s.onNext(response.getData());
} catch (Exception e) {
System.out.println("Inside Exception CITY NEW");
s.onError(e);
}
s.onComplete();
});
observable.onErrorReturnItem(new HashMap<>());
return observable;
}
Another stateObs.. same code
then
Observable<Map<Integer, String>> cityNamesObser = locationMediator.getMultipleCitiesName(cityIds);
Observable<Map<Integer, String>> stateNamesObs = locationMediator.getMultipleStatesName(stateIds);
Observable<Map<String, Map<Integer, String>>> zip = Observable.zip(
cityNamesObser,
stateNamesObs,
(cityNamesMap, stateNamesMap) -> {
System.out.println("Zipiing in Thread==="+Thread.currentThread().getName());
Map<String, Map<Integer, String>> result = new HashMap<>();
result.put("cityNames", cityNamesMap);
result.put("stateNames", stateNamesMap);
return result;
});
zip.blockingSubscribe(r -> {
System.out.println("Zip Subscribe in Thread==="+Thread.currentThread().getName());
System.out.println(r);
}, e->{
System.out.println("On Error zip");
});
}
Now the issue, observable.onErrorReturnItem is not working. Not working meaning it is throwing errror instead of returing blank hashmap
But If I change my code to
Observable<Map<Integer, String>> cityNamesObser = locationMediator.getMultipleCitiesName(cityIds).onErrroReturnItem(Blank Map);
Observable<Map<Integer, String>> stateNamesObs = locationMediator.getMultipleStatesName(stateIds).onErrroReturnItem(Blank Map);
Then it is working fine, meaning zip is returing blank hashmap with two keys cityNames and stateNames
Why is that? And do I make ny code work?
Ideally what I want to do is to return blank hashMap if any of my cityObs / stateObs gets failed. For that I want to trigger onError in case of Exception and attached onErrorReturnItem to Observable.

Related

How to pass a value from a synchronous method to asynchronous method within an apex class?

I have made a REST call to a url and need to pass the jsonresponse obtained from the synchronous method to the below asynchronous method.I have used a static variable - strresponse to do the same.But i am getting a null value when i call this value in the asynchronous method.Could this be because asynchronous methods do not remember the value given by a synchronous method? is there any alternate way this could be done?
code below:
public with sharing class myController {
public Static String strResponse ;
#AuraEnabled
public static Map<String, Object> retriveNews(){
HttpRequest httpRequest = new HttpRequest();
httpRequest.setEndpoint('endpoint URL');
httpRequest.setMethod('GET');
try{
Http http = new Http();
HttpResponse httpResponse = http.send(httpRequest);
if(httpResponse.getStatusCode() == 200){
newsController.strResponse = httpResponse.getBody();
} else {
throw new CalloutException(httpResponse.getBody());
}
} catch(Exception ex){
throw ex;
}
Map<String, Object> newsJsonData = new Map<String, Object>();
if(!String.isBlank(newsController.strResponse)){
System.debug('jsonstring:'+newsController.strResponse);
newsJsonData = (Map<String, Object>)JSON.deserializeUntyped(newsController.strResponse);
}
System.debug('jsonstring in retrivenews:'+newsController.strResponse);
if(!newsJsonData.isEmpty()){
return newsJsonData;
} else {
return null;
}
}
#future(callout=true)
public static void insertnews()
{
List<Newsroom__c> nrmlist = new List<Newsroom__c>();
System.debug('jsonstring in insertnews:'+newsController.strResponse);
JSONParser parser = JSON.createParser(newsController.strResponse);
while (parser.nextToken() != null) {
// Start at the array of invoices.
if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
while (parser.nextToken() != null) {
// Advance to the start object marker to
// find next invoice statement object.
if (parser.getCurrentToken() == JSONToken.START_OBJECT) {
objectne nrm = (objectne)parser.readValueAs(objectne.class);
objectne__c nroom = new objectne__c(Author__c = nrm.Author,Description__c = nrm.Description);
String s = JSON.serialize(nrm);
system.debug('Serialized object: ' + s);
nrmlist.add(nroom);
system.debug('list of nrooms:'+ nrmlist);
}
}
}
}
Database.SaveResult[] lsr = Database.insert(nrmlist, false) ;
System.debug(lsr);
}
Error - when newscontroller.insertnews() is called - null string to parser

Kafka custom state store is not getting updated

I am trying to build a custom state store which stores key to map of values.
Stream & Store configuration
final Serde<HashMap<String, ?>> userSessionsSerde = Serdes.serdeFrom(new HashMapSerializer(), new HashMapDeserializer());
StoreBuilder sessionStoreBuilder = Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(storeName),
Serdes.String(),
userSessionsSerde);
builder.addStateStore(sessionStoreBuilder);
builder.stream("connection-events", Consumed.with(Serdes.String(), wsSerde))
.transform(wsEventTransformerSupplier, storeName)
.to("status-changes", Produced.with(Serdes.String(), Serdes.String()));
KafkaStreams streams = new KafkaStreams(builder.build(), properties);
streams.start();
Transformer
public class WSEventProcessor implements Transformer<String, ConnectionEvent, KeyValue<String, String>> {
private String storeName = "user-sessions";
private KeyValueStore<String, Map<String, ConnectionEvent>> stateStore;
final Serde<HashMap<String, ?>> userSessionsSerde = Serdes.serdeFrom(new HashMapSerializer(), new HashMapDeserializer());
#SuppressWarnings("unchecked")
#Override
public void init(ProcessorContext context) {
this.context = context;
stateStore = (KeyValueStore<String, Map<String, ConnectionEvent>>) context.getStateStore(storeName);
}
#Override
public void close() {
}
#Override
public KeyValue<String, String> transform(String key, ConnectionEvent value) {
boolean sendUpdate = false;
//Send null if there are no updates to be sent to downstream processors
if(value.getState() == WebSocketConnection.CONNECTED) {
if(stateStore.get(key) == null) {
stateStore.put(key, new HashMap<>());
sendUpdate = true;
}
stateStore.get(key).put(value.getSessionId(), value);
return sendUpdate ? KeyValue.pair(key, "Online") : null;
}
else {
stateStore.get(key).remove(value.getSessionId());
int size = stateStore.get(key).size();
return stateStore.get(key).isEmpty() ? KeyValue.pair(key, "Offline") : null;
}
}
}
The state store always has 0 size map for each key irrespective of connected and disconnected events. Am I doing something wrong?
value object that you stored into stateStore.put(key, value) and stateStore.get(key) are different objects (as it serialized and then deserialized).
Your issue is related to modification of object returned from state store:
stateStore.get(key).put(value.getSessionId(), value) and stateStore.get(key).remove(value.getSessionId()). when you update object stateStore.get(key), it's actually not persisted to state store, only changes that object.
So, to fix your issue, calculate required value (in your case HashMap), and only after that apply stateStore.put(key, calculated_value). If you need to remove key-value from state store, use stateStore.put(key, null). Your transform method should look approximately like:
public KeyValue<String, String> transform(String key, ConnectionEvent value) {
Map<String, Object> valueFromStateStore = stateStore.get(key);
Map<String, Object> valueToUpdate = ofNullable(valueFromStateStore).orElseGet(Collections::emptyMap);
KeyValue<String, String> resultKeyValue = null;
//Send null if there are no updates to be sent to downstream processors
if(value.getState() == WebSocketConnection.CONNECTED) {
if(valueToUpdate.isEmpty()) {
resultKeyValue = KeyValue.pair(key, "Online");
}
valueToUpdate.put(value.getSessionId(), value);
}
else {
valueToUpdate.remove(value.getSessionId());
if (valueToUpdate.isEmpty()) {
resultKeyValue = KeyValue.pair(key, "Offline");
}
}
stateStore.put(key, valueToUpdate);
return resultKeyValue;
}

how to POST Json object to a webservice

I am trying to consume a webservice and post the JSON object as request in my program. JSON is nested.
{
"paymentorder": {
"operation": "Purchase",
"currency": "NOK",
"amount": 15610,
"vatAmount": 3122,
"description": "Test Purchase",
"userAgent": "Mozilla/5.0...",
"language": "nb-NO",
"urls": {
"hostUrls": ["https://localhost:9002", "https://powertools.local:9002"],
"completeUrl": "https://powertools.local:9002/payment-completed",
"cancelUrl": "https://powertools.local:9002/payment-canceled",
"callbackUrl": "https://powertools.local:9002/payment-callback",
"termsOfServiceUrl": "https://powertools.local:9002/termsandconditoons.pdf"
},
"payeeInfo": {
"payeeId": "20f3341c-e570-40a1-b76f-5347f4866de8",
"payeeReference": "P4555334",
"payeeName": "Kiran Vemula",
"productCategory": "P00432101",
"orderReference" : "P45553234"
},
"payer": {
"consumerProfileRef": "63adb0760ebdcca15d8475773a59c3f3b03df6222dfcc9f5740ce1eb3465f58e"
}
}
}
the build the Hashmaps like below:
private Map<String, Object> initiatePaymentMenuRequestBody(){
final Map<String, Object> paymentorderChilds = new LinkedHashMap<String, Object>();
paymentorderChilds.put("operation", "Purchase");
paymentorderChilds.put("currency",currency);
paymentorderChilds.put("amount",amount);
paymentorderChilds.put("vatAmount",vatAmount);
paymentorderChilds.put("description",description);
paymentorderChilds.put("userAgent",userAgent);
paymentorderChilds.put("language",language);
paymentorderChilds.put("urls", initiatePaymentMenuURLs());
paymentorderChilds.put("payeeInfo", initiatePaymentMenuPayeeInfo());
paymentorderChilds.put("payer", initiatePaymentMenuPayer());
return paymentorderChilds;
}
private Map initiatePaymentMenuURLs(){
final Map<String, Object> initiatePaymentMenuURLs = new LinkedHashMap<String, Object>();
List<String> hostUrls = new ArrayList<>();
hostUrls.add(mediqHostUrls1);
hostUrls.add(mediqHostUrls2);
initiatePaymentMenuURLs.put("hostUrls",hostUrls);
initiatePaymentMenuURLs.put("completeUrl",completeUrl);
initiatePaymentMenuURLs.put("cancelUrl",cancelUrl);
initiatePaymentMenuURLs.put("callbackUrl",callbackUrl);
initiatePaymentMenuURLs.put("termsOfServiceUrl",termsOfServiceUrl);
return initiatePaymentMenuURLs;
}
// implement this method with the real data from B2CCustomer and Cart object
private Map initiatePaymentMenuPayeeInfo(){
Map<String, String> initiatePaymentMenuPayeeInfo = new LinkedHashMap<String, String>();
initiatePaymentMenuPayeeInfo.put("payeeId",metchantID);
initiatePaymentMenuPayeeInfo.put("payeeReference",payeeReference);
initiatePaymentMenuPayeeInfo.put("payeeName",payeeName);
initiatePaymentMenuPayeeInfo.put("productCategory",productCategory);
initiatePaymentMenuPayeeInfo.put("orderReference",orderReference);
return initiatePaymentMenuPayeeInfo;
}
private Map initiatePaymentMenuPayer(){
Map<String, String> initiatePaymentMenuPayer = new LinkedHashMap<String, String>();
initiatePaymentMenuPayer.put("consumerProfileRef", initiateConsumerSession());
return initiatePaymentMenuPayer;
}
and finally calling the webservice to post the data:
#Override
public String initiatePaymentMenu(PaymentOrder paymentOrder1) {
final RestTemplate restTemplate2 = new RestTemplate();
final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(host+initiatePaymentMenuhostpostfix);
Map paymentOrder = new HashMap();
paymentOrder.put("paymentorder", initiatePaymentMenuRequestBody());
final HttpEntity entity = new HttpEntity(paymentOrder,getHeadders());
LOG.info("initiatePaymentMenu===========> "+entity.getBody());
ResponseEntity<String> payExInitiatePaymentMenuResponse = restTemplate2.postForEntity(builder.build().encode().toUri(),entity,String.class);
LOG.info("initiatePaymentMenu" +payExInitiatePaymentMenuResponse.getStatusCode());
String returnString = payExInitiatePaymentMenuResponse.getStatusCode().toString();
return returnString;
}
Is I am doing the correct way? I am not getting the response and giving me 400 error. Is entity.getBody() prints the exact JSON? can I use it in postman to check the response?
Thanks in advance.
Solved. The web service is not accepting 2 different URLs in hostUrls field. It was a bug in the web service provider itself.

How to upload empty file with Retrofit 2?

I successfully set up an uploading images to my REST API via retrofit with this code:
File imageFile = ImagePicker.getFileFromResult(this, resultCode, data);
RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), imageFile);
MultipartBody.Part body =
MultipartBody.Part.createFormData("userpic", imageFile.getName(), requestFile);
RetrofitClient.compositeSubscription.add(RetrofitClient.getService().updateProfileUserpic("Token " + RevolutionApp.getInstance().getUserToken(), body).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Profile>() {
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
#Override
public void onNext(Profile profile) {
mProfileFragment.fetchProfileData();
}
}));
Now I need to send empty file (null) to my my API. How to implement it?
MultipartBody.Part.createFormData("userpic", imageFile.getName(), requestFile)
does not work
i don't Know this is the right way to do this,i tried like this.its working
MultipartBody.Part fileToUpload = null;
if (realPath != null) {
try {
File file = new File(realPath);
if (file.exists()) {
RequestBody requestBody = RequestBody.create(MediaType.parse("*/*"), file);
fileToUpload = MultipartBody.Part.createFormData("attachment", file.getName(), requestBody);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}else{
RequestBody attachmentEmpty = RequestBody.create(MediaType.parse("text/plain"), "");
fileToUpload = MultipartBody.Part.createFormData("attachment", "", attachmentEmpty);
}
Note: File Name should not be null ,in place of null use empty("").
Here is final solution:
API interface method to update model:
public void updateProfileData(ProfilePost profile, final OnProfileUpdatedListener listener) {
Observable<Profile> observable;
if (profile.getUserpicPart() != null) {
observable = RetrofitClient.getService().updateProfileData(profile.asHashMap(), profile.getUserpicPart());
} else {
observable = RetrofitClient.getService().updateProfileData(profile.asHashMap());
}
RetrofitClient.compositeSubscription.add(observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Profile>() {
...
}));
}
Retrofit method signature:
#PUT("/api/v1/profile/")
#Multipart
Observable<Profile> updateProfileData(#Header("Authorization") String authorization, #PartMap() Map<String, RequestBody> partMap, #Part MultipartBody.Part image);
#PUT("/api/v1/profile/")
#Multipart
Observable<Profile> updateProfileData(#Header("Authorization") String authorization, #PartMap() Map<String, RequestBody> partMap);
and model to post:
public class ProfilePost {
MultipartBody.Part userpicPart;
public HashMap<String, RequestBody> asHashMap() {
HashMap<String, RequestBody> result = new HashMap<>();
result.put("first_name", RequestBody.create(MediaType.parse("text/plain"), this.firstName));
result.put("last_name", RequestBody.create(MediaType.parse("text/plain"), this.lastName));
result.put("email", RequestBody.create(MediaType.parse("text/plain"), this.email));
}
public void getUserpicPart() {
if (this.userpicFile == null) {
return null;
}
return MultipartBody.Part.createFormData("userpic", this.userpicFile.getName(), requestFile);
}
}

How to enrich the payload with an object from MongoDB (camel-mongodb)

I'm trying to pull object from MongoDb and ADD it to my current payload and save it in another database:
#Override
public void configure() throws Exception
{
from(kafkaEndpoint)
.convertBodyTo(DBObject.class)
.enrich("mongodb:mongoDb?database=myDbName1&collection=UserColl&operation=findOneByQuery",
(original, external) -> {
DBObject originalBody = original.getIn().getBody(DBObject.class);
DBObject externalBody = external.getIn().getBody(DBObject.class);
Map<String, DBObject> map = new HashMap<String, DBObject>();
map.put("original", originalBody);
map.put("external", externalBody);
original.getIn().setBody(map);
return original;
})
.to("mongodb:mongoDb?database=myDbName2&collection=UserColl&operation=insert");
}
The problem that enrich fetch the query from the In.body that holds my original object...
So how can I pass query ({"entity.id": ""}) to enrich(mongoldb:...) and preserve original object to merge it with results?
Thanks.
#Override
public void configure() throws Exception
{
from(kafkaEndpoint)
.convertBodyTo(DBObject.class)
.enrich("direct:findOneByQuery", // <-------
(original, external) -> {
DBObject originalBody = original.getIn().getBody(DBObject.class);
DBObject externalBody = external.getIn().getBody(DBObject.class);
Map<String, DBObject> map = new HashMap<String, DBObject>();
map.put("original", originalBody);
map.put("external", externalBody);
original.getIn().setBody(map);
return original;
})
.to("mongodb:mongoDb?database=myDbName2&collection=UserColl&operation=insert");
}
from("direct:findOneByQuery")
.process(new Processor()
{
#Override
public void process(Exchange exchange) throws Exception
{
DBObject body = exchange.getIn().getBody(DBObject.class);
DBObject query = BasicDBObjectBuilder.start()
.append("entity._id", body.get("_id"))
.get();
exchange.getIn().setBody(query);
}
})
.to("mongodb:mongoDb?database=myDbName1&collection=UserColl&operation=findOneByQuery");
//