SAPUI5 Send oData Request to Changeset - odata

I would like to send a batch request to the changeset in the backend from my UI5 application. I did the following:
I created an Entity in my segw service. In the "changeset_begin" method I set the cv_defer_mode to true for my Entity.
In the frontend I tried to send a call to the backend. But somehow it doesnt work and I cant set a breakpoint in the ChangeSet. Are my syntax wrong? Thank you very much!
oDataModel.create("/MyEntitySet", { // in a loop, values are changed
properties: {
Key: item[i].getKey(),
Salesorder:"347854"
Department: "HR"
}
});
oDataModel.submitChanges({ // executed after loop
success: function (oData) {
oDataModel.refresh();
}
});

According to the API the method you are looking for is createEntry.
Creates a new entry object which is described by the metadata of the entity type of the specified sPath Name. For each created entry a request is created and stored in a request queue. The request queue can be submitted by calling submitChanges

Related

Get Response from CREATE_STREAM

We upload a document from SAPUI5 to our SAP System using the CREATE_STREAM Method of the oData Service in ABAP. The creation of the document works fine.
What we would like to achieve is to get the response back to SAPUI5. Especially when there is an error during the creation of the document in the backend.
In Frontend we use the uploadSet Control.
...oUploadSet.uploadItem(oItem);
In the Backend we create a message with
...lo_message_container->add_message( iv_msg_type = /iwbep/cl_cos_logger=>error
iv_msg_number = '018'
iv_msg_id = lv_msg_id
iv_add_to_response_header = abap_true
)....
We can find the created message in the error protocol of our gateway server (/IWFND/ERROR_LOG). But how can this message be retrieved in SAPUI5 and used in the MessageManger Control?
We tried the onUploadCompleted Control but we can't find any response data there.
Can somebody explain how the response or a message header from the CREAT_STREAM method can be used in SAPUI5?
The "new" UploadSet control is kinda half-baked imo. The response will get lost in some internal method. This internal method will then trigger onUploadCompleted and you get nothing but useless information.
Lucky for us we can easily overwrite this internal stuff. UploadSet has an aggregation Uploader. We have to provide our own Uploader. Problem solved. Here is the line that needs to be modified.
sap.ui.define([
"sap/m/upload/Uploader",
...
], function (Uploader, ...) {
return Uploader.extend("my.custom.control.Uploader", {
uploadItem: function (oItem, aHeaders) {
// beginning of the method. take it from the official sources
oXhr.onreadystatechange = function () {
const oHandler = that._mRequestHandlers[oItem.getId()];
if (this.readyState === window.XMLHttpRequest.DONE && !oHandler.aborted) {
// we need to return the xhr object. it contains the response!
that.fireUploadCompleted({ item: oItem, xhr: oXhr });
}
};
// .. rest of the method
}
});
});
Use it like this
<mvc:View xmlns:custom="my.custom.control" ....>
<UploadSet items="....">
.....
<uploader>
<custom:Uploader uploadUrl="......"
uploadCompleted=".onUploadCompleted"
uploadStarted=".onUploadStarted" />
</uploader>
</UploadSet>
Edit: Your own uploader also means implementing your own event handlers (uploadAborted, uploadCompleted, uploadProgressed, uploadStarted). See the official documentation for more information about the events.

SAP Gateway get create ID as response

I'm using OData services to send data to an SAP system.
When I'm doing a
oModel.create({data})
Is there any way to let the server give me as response the ID or field I just created in the database?
Any suggestion is welcome.
Yes, this is a very common use case.
Model the id as a field of your entity.
Leave the id empty when making the call from the Frontend.
The server fills the id field in the response.
In the response, the server may also change or fill any other field of the entity.
in your backend method MYENTITY_CREATE_ENTITY, do something like
io_data_provider->read_entry_data( IMPORTING es_data = ls_myentity ).
"create object in database which fills the field lv_id_from_database
ls_myentity-id = lv_id_from_database.
er_entity = ls_myentity.
Frontend:
oModel.create("/Myentity",
oDataCreate, null, false,
function(oData, oResponse){
//Function for Success
},
function(oData, oResponse){
//Function for Error
});

$batch request resulting in error "Default changeset implementation allows only one operation"

I am making a worklist application using SAPUI5. The problem is that when I create an entry and then create another one right after that, I get the following error:
Default changeset implementation allows only one operation.
I checked the $batch header and I see that there is a MERGE and a POST, with the MERGE updating the previous entry for some reason. Can anyone shed some light? Could it be a backend error and not a UI5 error?
Creating the new entry:
_onMetadataLoaded: function() {
var oModel = this.getView().getModel();
var that = this;
// ...
oModel.read("/USERS_SET", {
success: function(oData) {
var oProperties = {
Qmnum: "0",
Otherstuff: "cool"
};
that._oContext = that._oView.getModel().createEntry("/ENTITYSET", {
properties: oProperties
});
that.getView().setBindingContext(that._oContext);
// ...
}
});
},
handleSavePress: function(oEvent) {
// ...
this.getView().getModel().submitChanges({
success: function(oData) {
// ...
},
error: function(oError) {
// ...
}
});
},
tl-dr: Apparently you must be using the SAP Gateway. If you do not need to process those requests in one transaction then send them in different changesets. If you do not need batch calls at all consider turning it off by supplying your model with "useBatch": false upon instantiation. However if you need to process the requests together in one transaction then you have to read the details below.
In order to understand the problem you have to understand how the gateway and the batch and changeset requests work.
Batch requests consist of multiple requests bundled together. The purpose is to open only one connection and group together relevant requests so that the overhead is minimalized. Changesets form smaller blocks inside batch requests, where modification requests can be bundled and processed together in order to ensure an all-or-nothing characteristic.
So on the gateway side: there are two relevant classes for your OData service, assuming that you have used the SAP Gateway Service Builder (SEGW transaction). There is one with the ending ...DPC and one with ...DPC_EXT. Don't touch the former, it will be always regenerated when you update your service in the service builder. The latter is the one that we will need in this example. You will have to redefine at least two methods:
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS
By default the changeset_begin method will only allow changeset processing for changesets where the number of requests equals to one. This can be handled automatically that's why a limitation exists. If there were more requests one could not ensure their processing automatically as they could have a business dependency on each other.
So make sure to allow a bundled (deferred mode) processing of changesets under the desired conditions:
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN: first call the super->/iwbep/if_mgw_appl_srv_runtime~changeset_begin method in a try catch block, then loop at it_operation_info to decide and narrow down processing only in selected cases and then allow cv_defer_mode only for the selected cases, otherwise throw a /iwbep/cx_mgw_tech_exception=>changeset_not_supported exception.
/IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS: all requests will be available in the it_changeset_request. Make sure to fill the ct_changeset_response table with the responses.
METHOD /iwbep/if_mgw_appl_srv_runtime~changeset_process.
DATA:
lv_operation_counter TYPE i VALUE 0,
lr_context TYPE REF TO /iwbep/cl_mgw_request,
lr_entry_provider TYPE REF TO /iwbep/if_mgw_entry_provider,
lr_message_container TYPE REF TO /iwbep/if_message_container,
lr_entity_data TYPE REF TO data,
ls_context_details TYPE /iwbep/if_mgw_core_srv_runtime=>ty_s_mgw_request_context,
ls_changeset_response LIKE LINE OF ct_changeset_response.
FIELD-SYMBOLS:
<fs_ls_changeset_request> LIKE LINE OF it_changeset_request.
LOOP AT it_changeset_request ASSIGNING <fs_ls_changeset_request>.
lr_context ?= <fs_ls_changeset_request>-request_context.
lr_entry_provider = <fs_ls_changeset_request>-entry_provider.
lr_message_container = <fs_ls_changeset_request>-msg_container.
ls_context_details = lr_context->get_request_details( ).
CASE ls_context_details-target_entity.
WHEN 'SomeEntity'.
"Do the processing here
WHEN OTHERS.
ENDCASE.
ENDLOOP.
ENDMETHOD.
From the error I can tell you must be using SAP GW :-) This happens only for batch requests containing more than one create/delete/update calls and it's related to transaction security ("all or nothing"). What you have to do is redefining the corresponding GW method, I think it was CHANGESET_BEGIN. See https://archive.sap.com/discussions/thread/3562720 for some details (can't offer more for now...).

OData : Why am I getting HTTP 428 (Precondition Required) error while performing an update

So here's my code
sap.ui.getCore().getModel("myModel").update("/ZSystemNameSet(mandt='001')", data, null, function(datay, responsey){
sap.ui.getCore().getModel().refresh();
MessageToast.show("It worked...!! Data: "+datay+"Response: "+responsey);
}, function(datax,responsex){
MessageToast.show("Sorry! Data: "+datax+"Response: "+responsex);
});
Also how do I add the header attributes to the update() call?
Obviously your service uses optimistic locking and expects an If-Match header, containing the ETag of the entity, in the request. You can pass this ETag as parameter to the update method. For further details you should check your service definition and the documentation.
Regarding the update of header attributes: It is hard do answer as there is no information regarding your entity orchestration. Normally you should be able to add a property containing the update information for you header to the data structure you send to the server, e.g. if the header is reachable from your entity ZSystemName via association "Header" you do the following:
data.Header = { "attribute1" : value1, "attribute2" : value2 }

get 'undefined' saveResult.entities after saveChanges [breezejs]

I tried to update one entity in my angularjs client using breezejs library. After calling saveChanges(), it can actually save back in the server and fetched on the client. However, the server did not return the response back. The saveResult.entities is undefined and pop up an error for me. When I took a look at the docs, it mentions 'Some service APIs do not return information about every saved entity. If your server doesn't return such information, you should add the pre-save, cached entity to saveResult.entities yourself'. Could anyone provide an example of how to do this?
This is the code when i am trying to do an update.
manager.saveChanges(entitiesToSave, null, (saveResult) => {
const savedRes = saveResult;
savedRes.entities = entitiesToSave;
return savedRes;
}).then(saveSucceeded);
On the server, you would need to construct the response for an update similar to the way it is for a create:
response.setContent(...); // entities
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());

Resources