I have an SAPUI5 application which is deployed to ABAP server and accessible with Fiori Launchpad. I use this app to create a new interaction (OData Service CUAN_IMPORT_SRV) in Hybris Marketing. My problem is that even though the OData call created no new entry (because such entry already exists), I get the success message. When I add invalid data to the upload data, I get the error message.
This is my code:
var oModel = new sap.ui.model.odata.v2.ODataModel("https://hostname:port/sap/opu/odata/sap/CUAN_IMPORT_SRV/", true);
var oData = { some json... }
oModel.create("/ImportHeaders", oData, {
success: function() {
sap.m.MessageBox.success("Interaction successfully created!", {
title: "Success"
});
},
error: function() {
sap.m.MessageBox.error("Interaction could not be created.", {
title: "Error"
});
}
});
When I run /n/iwfnd/traces it is marked as "successful execution" (even though no new entry was created).
How can it be that the success message appears even though no new entry was created? How can I avoid that?
First thing is to add your business error in the ABAP backend:
DATA:
lt_bapi_return type table of bapiret2,
lo_message_container type ref to /iwbep/if_v4_message_container.
#Error handling
if lt_bapi_return is not initial.
#check if an error message is in lt_bapi_return
loop at lt_bapi_return into ls_bapi_return.
if ls_bapi_return-type = 'E'.
lo_message_container = io_response->get_message_container( ).
loop at lt_bapi_return into ls_bapi_return.
lo_message_container->add_t100(
exporting
iv_msg_type = ls_bapi_return-type
iv_msg_id = ls_bapi_return-id
iv_msg_number = ls_bapi_return-number
iv_msg_v1 = ls_bapi_return-message_v1
iv_msg_v2 = ls_bapi_return-message_v2
iv_msg_v3 = ls_bapi_return-message_v3
iv_msg_v4 = ls_bapi_return-message_v4 ).
endloop.
"raise exception
raise exception type zcx_e2e001_odata_v4_so
exporting
message_container = lo_message_container.
endif.
endloop.
endif.
And at UI:
error: function(response) {
//response will have message details
//each message can have business text, technical info, error code.
sap.m.MessageBox.error("Interaction could not be created.", {
title: "Error"
});
}
You can add this part of code to every redefined method..
better is create a util method and reuse.
Related
I am using FSharp.Data.GraphQL for implementing GraphQL server. The documentation is not very clear about error handling. In the following mutation resolver, I wish to return errors if user provides invalid values for email and password:
let login =
Define.AsyncField(
name = "login",
typedef = Nullable Output.Token,
description = "Login using email and password",
args = [
Define.Input("email", String)
Define.Input("password", String)
],
resolve =
fun ctx _ ->
// ... some more code related to db and validation
let customErr: exn = Error.InvalidUserPassword
// This statement has not effect
ctx.AddError customErr
// Should I use raise to explicitly throw exception
// raise customErr
Async.result None
)
Looking into the source code, I found out AddError method on ResolveFieldContext. But it seems to produce no effect on the final result of the mutation. The output is always without any error:
{
"documentId": 1234,
"data": {
"login": null
},
// The errors array is not available
"errors": []
}
Should I raise the exception to add the error handling? And, if that is the case, how can I go about adding multiple error codes as part of single query or mutation since I can raise only one exception?
I'm trying to use OData V2 as suggested in this comment.
The issue is whenever I use sap.ui.model.odata.v2.ODataModel rather than the deprecated sap.ui.model.odata.ODataModel, I get this error from SAP Gateway Error Log
The Data Services Request could not be understood due to malformed syntax
Controller:
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/model/odata/v2/ODataModel",
// ...,
], function(Controller, ODataModel/*, ...*/) {
"use strict";
Here is when I called OData V2:
onPressButton1: function(){
var vEntityURL = "/CustomerSet(ID='000')";
var sServiceUrl = "/Customers_SRV/";
var oServiceModel = new ODataModel(sServiceUrl, true);
oServiceModel.read(vEntityURL, {
success: function(oData) {
// ...
}
});
},
This is batch error.
Your entity set is not supporting batch calls.
Make sure that allows batch call or set use batch - false as below,
oServiceModel.setUseBatch(false);
This will work for you.
I'm trying to display errors in a friendly way, but I'm always getting the errors stack trace with console logs that I want to get rid of.
The idea is to create a Lead in our platform using any source, for example, Google Sheets.
When an invalid email is provided in the lead and posted to our API, I'm getting the expected message I want to display followed by the stack trace.
My custom error message is
INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail.
But this is what I'm getting:
INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail. What happened: Starting POST request to https://cosmo-charon-production.herokuapp.com/v1/lead/vehicle Received 500 code from https://cosmo-charon-production.herokuapp.com/v1/lead/vehicle?api-key=gIBp04HVdTgsHShJj6bXKwjbcxXTogsh after 62ms Received content "{"code":"SCHEMA_VALIDATION_FAILED","message":"Request validation failed: Parameter (lead) failed sch" INVALID FORMAT for email. Object didn't pass validation for format email: as1#mail. Console logs:
Image showing error displayed in Zapier
I've added a middleware for ErrorHandling into afterResponse, just as one of the examples provided in Zapier docs.
The function analyzeAndParse() receives an error object from the API and returns a string with the error message translated in a friendly way
const checkForErrors = (response, z) => {
// If we get a bad status code, throw an error, using the ErrorTranslator
if (response.status >= 300) {
throw new Error(analyzeAndParse(response.json))
}
// If no errors just return original response
return response
}
This is the code that creates a Lead in our platform, making a request to our API.
function createLead (z, bundle) {
const industry = bundle.inputData.industry
// add product to request based on the inputFields
leadType[industry].addProductFields(bundle.inputData)
const requestOptions = {
url: `${baseUrl}lead/${_.kebabCase(industry)}`,
method: 'POST',
body: JSON.stringify(checkSourceForCreate(bundle.inputData)),
headers: {
'content-type': 'application/json'
}
}
return z.request(requestOptions).then((response) => {
if (response.status >= 300) {
throw new Error(analyzeAndParse(response.content))
}
const content = JSON.parse(response.content)
if (content && content.leads) {
// get only the last lead from the list of leads
content.lead = content.leads[0]
delete content.leads
}
return content
})
}
Any ideas?
Thanks!
I am trying send some data to sap gateway service.
I am using this example the method "save", but when I try do it in my code I get an error "OData is not defined"
Below is the method when I try do it.
handleConfirmationMessageBoxPress: function(oEvent) {
var bCompact = !!this.getView().$().closest(".sapUiSizeCompact").length;
MessageBox.confirm(
"Deseja confirmar a transferência?", {
icon: sap.m.MessageBox.Icon.SUCCESS,
title: "Confirmar",
actions: [sap.m.MessageBox.Action.OK, sap.m.MessageBox.Action.CANCEL],
onClose: function(oAction) {
if (oAction == "OK") {
var oParameters = {};
oParameters.loginfrom = this.getView().byId("multiInput").getValue();
oParameters.loginfrom = this.getView().byId("loginPara").getValue();
oParameters.loginfrom = this.getView().byId("datade").getValue();
oParameters.loginfrom = this.getView().byId("datapara").getValue();
OData.request({
requestUri : "http://<host name>:<port no>/sap/opu/odata/sap/ZMM_EMP_SRV/EmployeeSet",
method : "GET",
headers : {...}
},
function(data, response) {
...
var oHeaders = {
... };
OData.request({
requestUri : "http://<host name>:<port no>/sap/opu/odata/sap/ZMM_EMP_SRV/EmployeeSet",
method : "POST",
headers : oHeaders,
data:oParameters
},
function(data,request) {
MessageToast.show("Transferência realizada!");
location.reload(true);
}, function(err) {
MessageToast.show("A transferência falhou!");
});
}, function(err) {
var request = err.request;
var response = err.response;
alert("Error in Get — Request " + request + " Response " + response);
});
} else {
...
You are attempting to use the OData global object from the datajs library. This library is indeed shipped with OpenUI5, but IMO you should not use it directly (but use the methods of the OData model; there is no real guarantee that UI5 will continue shipping this third-party library in the future).
You are most likely getting the error because the library was not yet loaded by UI5. Libraries are generally lazily loaded by UI5, so you will have to request that UI5 loads it for you (in the tutorial that you have linked, it was loaded behind the scenes by the OData model). To do this, you can either use jQuery.sap.require (jQuery.sap.require("sap.ui.thirdparty.datajs")) or list the dependency inside your sap.ui.define call at the beginning of the controller (e.g. sap.ui.define(['sap/ui/thirdparty/datajs'], function(datajs){...})).
Later edit: you can also use the jQuery.sap.require("sap.ui.model.odata.datajs"); call, but the module was moved from there and it would effectively redirect you to the new location.
this is a very old example, and the used old techniques.
You should add this line to your code:
jQuery.sap.require("sap.ui.model.odata.datajs");
This should solve your oData is undefined problem.
In general you should read newer examples where the read() function of the odata model is used.
I built an ajax chat in one of my mvc website. everything is working fine. I am using polling. At certain interval i am using $.post to get the messages from the db. But there is a problem. The message retrieved using $.post keeps on repeating. here is my javascript code and controller method.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
$("#hdnLastMsgRec").val(Data.LastMsgRec);
}, "json");
t = setTimeout("GetMessages()", 3000);
}
and here is my controller method to get the data:
public JsonResult GetMessages(int roomId,DateTime lastRecMsg)
{
StringBuilder messagesSb = new StringBuilder();
StringBuilder newUserSb = new StringBuilder();
List<Message> msgs = (dc.Messages).Where(m => m.RoomID == roomId && m.TimeStamp > lastRecMsg).ToList();
if (msgs.Count == 0)
{
return Json(new { Messages = "", LastMsgRec = System.DateTime.Now.ToString() });
}
foreach (Message item in msgs)
{
messagesSb.Append(string.Format(messageTemplate,item.User.Username,item.Text));
if (item.Text == "Just logged in!")
newUserSb.Append(string.Format(newUserTemplate,item.User.Username));
}
return Json(new {Messages = messagesSb.ToString(),LastMsgRec = System.DateTime.Now.ToString(),newUser = newUserSb.ToString().Length == 0 ?"":newUserSb.ToString()});
}
Everything is working absloutely perfect. But i some messages getting repeated. The first time page loads i am retrieving the data and call GetMessages() function. I am loading the value of field hdnLastMsgRec the first time page loads and after the value for this field are set by the javascript.
I think the message keeps on repeating because of asynchronous calls. I don't know, may be you guys can help me solve this.
or you can suggest better way to implement this.
Kaivalya is correct about the caching, but I'd also suggest that your design could and should be altered just a tad.
I made a very similar app recently, and what I found was that my design was greatly enhanced by letting the controllers work with the fairly standard PRG pattern (post-redirect-get). Why enhanced? well, because POST methods are built to add stuff to an app, GET methods are supposed to be used to get information without side effects. Your polling should be just getting new messages w/o side effects.
So rather than your $.post call expecting data and handling the callback, what I'd recommend is having your controller expose a method for creating new chat messages via POST and then another method that get the last X chat messages, or the messages since a certain timestamp or whatever.
The javascript callback from the post action, then can update some variables (e.g. the last message id, timestamp of the last message, or even the whole URL of the next message based on the info contained in a redirect, whatever).
The $.post would fire only in response to user input (e..g type in a box, hit 'send') Then, you have (separately) a $.get call from jquery that's set up to poll like you said, and all it does is fetch the latest chat messages and it's callback updates the chat UI with them.
I got my answer here: ASP.NET AJAX CHAT
The names below i am referring to are from above link.
i think the actual problem was with the timestamp thing and asynchronous behaviour of $.post. after calling "GetMessages()" method, even if the previous request to retrive chat message was not complete anathor call to same method used to fire due to setting timeout for "GetMessages()" method outside the $.post method. In my question you can see that timeout for "GetMessages()" method is set outside the $.post method. Now i set the timeout for "GetMessages()" method inside the $.post method. so that next call to "GetMessages()" only occur after 3 seconds of completion of current $.post method. I have posted the code below.
var t;
function GetMessages() {
var LastMsgRec = $("#hdnLastMsgRec").val();
var RoomId = $("#hdnRoomId").val();
//Get all the messages associated with this roomId
$.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
if (Data.LastMsgRec.length != 0)
$("#hdnLastMsgRec").val(Data.LastMsgRec);
if (Data.Messages.length != 0) {
$("#messagesCont").append(Data.Messages);
if (Data.newUser.length != 0)
$("#usersUl").append(Data.newUser);
$("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
$("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
}
else {
}
t = setTimeout("GetMessages()", 3000);
}, "json");
}
I addition to that i also changed few things. As suggested by ignatandrei i placed $("#hdnLastMsgRec").val(Data.LastMsgRec); immediately after function(Data) {.
and also
as said by MikeSW i changed the data retrieval process. Previously i was extracting data on the basis of timespan(retrieve all the data associated with
this room id that has greater timespan than last data retrieved message timespan) but now i keep track of the messageid. Now i retrieve only those data that
has message id greater than last retrieved message id.
and guess what no repeataion and perfectly working chat application so far on my intranet.
I still got to see it's performance when deployed on internet.
i think it solved my problem.
i will still test the system and let u guys know if there is any problem.
By default $.post() caches the results
You can either call $.ajaxSetup ({ cache: false}); before JS GetMessages function call to ensure caching is disabled or change the $.post to $.ajax and set cache attribute to false. In the end $.post() is a short cut to this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: success
dataType: dataType
});