Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
kenichi_unnai
Advisor
Advisor

This page is for explaining how to test the OData $batch services with Content ID, which is implemented in the H2G.


In this H2G, we're going through the steps of testing OData $batch services with Content ID by REST Client.


In the test steps, we'll create a $batch request that contains one parent entity with Content ID $100, and two children.

Two testing tools are explained:

SAP Gateway Client


In the SAP tx SEGW, find your Gateway project and select "Service Maintenance" > "Your GW System" > "SAP Gateway Client".

In a SAP Gateway Client tool, enter following request information:


Note: SAP Gateway Client handles all the authentication info such as X-CSRF-Token or Basic Auth behind the scenes.


  • HTTP Method = POST
  • Request URI = the URL has $batch suffix
  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • Payload body = enter the batch script

And execute it - confirm the successful response.

POSTMAN REST client


Start a POSTMAN REST client. Type the URL of your OData service document URL (the URL that ends with "_SRV/"). Set following params:

  • HTTP GET
  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • X-CSRF-Token = Fetch
  • Authentication - (basic auth credentials)

Execute the request by pressing "Send". You should be able to obtain the returned x-csrf-token value. Copy it - this is required to execute HTTP POST later.

Paste the token value for X-CSRF-Token param in the header. Set following params:


  • HTTP POST - the URL has $batch suffix
  • Content-Type = multipart/mixed;boundary=batch_mybatch
  • X-CSRF-Token = <token>
  • Authentication - (basic auth credentials)

Now your POSTMAN is fully ready for sending the $batch payload. Click on "Body" and select raw.

Here you'll enter the payload. (Explanation follows)

01 --batch_mybatch
02 Content-Type: multipart/mixed; boundary=changeset_mychangeset1
03
04 --changeset_mychangeset1
05 Content-Type: application/http
06 Content-Transfer-Encoding: binary
07
08 POST HeaderSet HTTP/1.1
09 Content-Type: application/xml
10 Content-Length: 1021
11 Content-ID: 100
12
13 <?xml version="1.0" encoding="utf-8"?>
14 <entry xml:base="http://..._SRV/"
15     xmlns="http://www.w3.org/2005/Atom"
16     xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
17     xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
18     <content type="application/xml">
19         <m:properties>
20             <d:Id>1</d:Id>
21             <d:Text>New parent entity</d:Text>
22         </m:properties>
23     </content>
24 </entry>
25
26 --changeset_mychangeset1
27 Content-Type: application/http
28 Content-Transfer-Encoding: binary
29
30 POST $100/ToItems HTTP/1.1
31 Content-Type: application/xml
32 Content-Length: 1021
33
34 <?xml version="1.0" encoding="utf-8"?>
35 <entry xml:base="http://..._SRV/"
36     xmlns="http://www.w3.org/2005/Atom"
37     xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
38     xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
39     <content type="application/xml">
40         <m:properties>
41             <d:ParentId></d:ParentId>
42             <d:Id>000010</d:Id>
43             <d:Text>First child entity</d:Text>
44         </m:properties>
45     </content>
46 </entry>
47
48 --changeset_mychangeset1
49 Content-Type: application/http
50 Content-Transfer-Encoding: binary
51
52 POST $100/ToItems HTTP/1.1
53 Content-Type: application/xml
54 Content-Length: 1021
55
56 <?xml version="1.0" encoding="utf-8"?>
57 <entry xml:base="http://..._SRV/"
58     xmlns="http://www.w3.org/2005/Atom"
59     xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
60     xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
61     <content type="application/xml">
62         <m:properties>
63             <d:ParentId></d:ParentId>
64             <d:Id>000020</d:Id>
65             <d:Text>Second child entity</d:Text>
66         </m:properties>
67     </content>
68 </entry>
69
70 --changeset_mychangeset1--
71
72 --batch_mybatch--

#01 - The beginning of the $batch request. It has to start with "--" with the batch name, which is the same name you defined in the header of Content-Type = multipart/mixed;boundary=batch_mybatch.

#02 - Content type definition of the changeset.

#03 - This line space is very important. At least one "carriage return line feed" is required.

#04 - The beginning of the changeset. It has to start with "--" with the changeset name, which is the same name you defined in the line #02.

#05/06 - Conventional value for Content-Type and Content-Transfer-Encoding.

#07 - At least one "carriage return line feed" is required.

#08/09 - POST request for HeaderSet entity.

#10 - Content-Length value must be equal to or greater than the real length of the data below.

#11 - A line of the Content ID value.

#12 - At least one "carriage return line feed" is required.

#13 to 24 - The HeaderSet payload. You can obtain this payload format via Read operation. If you obtain the payload via Read, that should contain the tags like <id/>, <title/>, <updated/>, <category/>, and <link/>. You can keep them in this $batch payload or just discard them - the OData services simply ignore them.

#25 - At least one "carriage return line feed" is required.

#26 - As the next entity is a part of the same changeset, it has the same value with the line #04.

#27 to 29 - Same rule follows in the ItemSet.

#30 - POST request for ItemSet entity. As you see, the Content ID is being used.

#31 to 33 - Same rule follows in the ItemSet.

#34 to 46 - The ItemSet payload. Just like you did for HeaderSet entity, you can obtain this payload format via Read operation. Please note this payload has the empty value for <parentID> tag. During OData $batch processing, this value will be handled correctly via the Content ID value.

#47 to 69 - The second HeaderItem POST request.

#70 - The closing of the changeset. It has to start with "--" with the changeset name AND closing "--" characters.

#71 - At least one "carriage return line feed" is required.

#72 - The closing of the batch request. It has to start with "--" with the batch name AND closing "--" characters.

Let's execute the $batch request by pressing Send button. If the $batch processing is done correctly, you should be able to see "201 Created" responses for each entity in the returned payload body.


Note: As written, the line spacing is critical (it is a part of the standard OData specification).

Time to experiment further... try set the remote breakpoint in the ABAP editor - and see how the changeset_begin, changeset_process, and changeset_end are called sequentially.

13 Comments
Former Member
0 Kudos
Hi  kenichi.unnai,

I exactly followed you steps for batch post but i am getting following error in the method.



 



 

Thanks

Siva
ealimeta
Participant
0 Kudos
Hello sivaprasath.sekar2 .

Firstly start your body code with the code above (replacing the existing code from line 1 to line 5)
--batch_123
Content-Type: multipart/mixed; boundary=changeset_123

 

--changeset_123
Content-Type: application/http
Content-Transfer-Encoding: binary

 

At the end the changeset and the batch must be closed

--changeset_123--
--batch_123--

 

Hope this would help  😉
ealimeta
Participant
0 Kudos
Hello kenichi.unnai .

Actually I am trying to use the batch to improve the performance of my custom ODATA service that creates SalesOrders using a Bapi .

The payload we are using is that of the type deep entity (each XML payload contains all the data of the header and item ).

I was wondering if there is a way to tell that I am using the $batch processing in order to set a different processing logic when we are in $batch.

Is there any chance i can have inside an internal table all the Payloads of the change sets so i can  populate all the tables of the BAPI in one shot and if it ever fails for any kind of error in any of the item , the posting (update/create) should result in an error and not be executed.

Actually the processing of the payloads is done sequentially , which means if I am going to create a document  with 4 positions and supose position 3 should block the document creation , we fristly create the document then for the second line we go on modify of that document adding an item and so on , but this is kind of wrong .

Because if one of the items should go wrong the document must not be created at alla, so thats why I need to have all the items in one Internal Table so i can do the create or update calling the BAPI only once per document .

Any idea or solution on this would be appreciated.

Thanks
Toni
kenichi_unnai
Advisor
Advisor
0 Kudos
Hi Toni,

Just caught up with your question.

 

Are you familiar with the LUW of ABAP? As all the $batch execution is in the same LUW scope, if you execute BAPIs inside, it will be rolled back in case any BAPI execution fails.

 

Best regards,

Ken
ealimeta
Participant
0 Kudos
Hi Ken.
Yes I am familiar with the Logical Unit of Work 🙂 .

In fact my main concern was how to handle all the payloads infos  in one internal table , but actually i solved my issue using 3 FMs  of the same Function Group .
1- Z_APPEND_BATCH "-> append payloads inside global ITAB of the FG
2- Z_RETRIVE_ITAB_BATCH "->retrive the ITAB with all payloads once we are at the last changeset .
3- Z_REFRESH_ITAB_BATCH "-> clear the ITAB for the next uses .

Inside the Loop of the table IT_CHANGESET_PROCESS  used to call my 1-st FM  in order to collect all the data in the GLOBAL Internal Table of the FG .

In order to find the last request we set a logic with a client so we decided to add a field to the structures of the payload that showed the last row of that particular SO .

So after looping all the changesets I had all the payloads inside my Itab and run the control on the last record in order to set the logic to populate the the BAPI's structures and tables right after the check.

Now I have only one question if you have experienced it before ,
Since I am collecting all the payloads before calling the BAPI at the last call , the method needs a response in the   IT_CHANGESET_RESPONSE  parameter ,
but unless I execute the BAPI cant know the fate of the SO if it will be created or will have problems, and still need to send a response back .
Can you please address any method how to send my own custom responses , of type S, I or W ,
Already tried some methods using message containers but the custom message wont display unless i throw an exception , which despite the type of message I used to create , turns on display an error message .

Thanks again.
Toni.
Former Member
0 Kudos
Hi,

I tried like u told. But its giving me 500: Internal Server error
ealimeta
Participant

Hi .

If it were a problem of the BATCH script formatting it would had returned the code 400 bad request,
Code 500 does not have to do with the formatting of the BATCH ,
check your header parameters please if are alimented correctly , if yes then put a break point in the method you are calling via the batch (POST  GET PUT) .

Thnx .
Toni

Former Member
Hi,

The Changeset_process method code was right. The only change I did now is, I given "--" instead of "-" in payload:

--batch_mybatch

Content-Type: multipart/mixed; boundary=changeset_mychangeset

 

--changeset_mychangeset

Content-Type: application/http

Content-Transfer-Encoding: binary

It's now working fine. Thanks for your help. Excellent post by kenichi.unnai
ealimeta
Participant
0 Kudos
You're welcome 😉
dm21
Participant
0 Kudos

Hello e.alimeta/Toni,

I am trying to pass Header and Line Item in the payload.

I could find the header in the changeset_begin method but I am not able to find the line item. It show only one entity type ie the header.

This is the payload  I am sending , Request you to help me on this

 

--batch
Content-Type: multipart/mixed; boundary=changeset_01869434-0005-0001

--changeset_01869434-0005-0001
Content-Type: application/http
Content-Transfer-Encoding: binary

POST SalesOrders HTTP/1.1
Content-Type: application/atom+xml
Content-Length: 1021

<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
<content type="application/xml">
<m:properties>
<d:Singleshipmentindicator>X</d:Singleshipmentindicator>
<d:Salesordersimulation>false</d:Salesordersimulation>
<d:Salesordernumber>0</d:Salesordernumber>
<d:Po/><d:Requesteddate>20170523</d:Requesteddate>
<d:Customerid>3272</d:Customerid>
<d:Salesorganization>3020</d:Salesorganization>
<d:Distributionchannel>30</d:Distributionchannel>
<d:Division>00</d:Division>
<d:Shipmentinstruction/>
<d:Notestoreceiver/>
<d:Shiptopartnerid>0000003272</d:Shiptopartnerid>
</m:properties>

</content>
</entry>

--changeset_01869434-0005-0001
Content-Type: application/http
Content-Transfer-Encoding: binary

POST OrderItems HTTP/1.1
Content-Type: application/atom+xml
Content-Length: 1021

<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
<content type="application/xml">
<m:properties>
<d:Quantity>1</d:Quantity>
<d:UnitofMeasure>PC</d:UnitofMeasure>
<d:RequestedDeliveryDate>20170523</d:RequestedDeliveryDate>
<d:Product>358</d:Product>
<d:SalesOrderNumber/>
<d:ItemNumber>000010</d:ItemNumber>
<d:Currency>USD</d:Currency>
</m:properties>

</content>
</entry>

--changeset_01869434-0005-0001--


--batch--

Thank you for your helpful information!!

We succeeded testing odata batch processing in our backend environment.

 
kenichi_unnai
Advisor
Advisor
0 Kudos
Hi Siva,

All I can tell you right here is to debug the gc_method_get. You'll be able to tell why you get the malformed syntax error..

 
nitish_chawla2
Participant
0 Kudos

Hello Kenichi,

 

I am getting 500 error. My payload is below. Could you please help me fixing the issue.

 

 


SPAN {
font-family: “Courier New”;
font-size: 10pt;
color: #000000;
background: #FFFFFF;
}
.L1S32 {
color: #7D9EC0;
}
.L1S33 {
color: #009300;
}
-batch_mybatch
Content-Type: multipart/mixed; boundary=changeset_mychangeset1

-changeset_mychangeset1
Content-Type: application/http
Content-Transfer-Encoding: binary

POST HeaderSet HTTP/1.1
Content-Type: application/xml
Content-Length: 1021
Content-ID: 100

<?xml version=“1.0” encoding=“utf-8”?>
<entry xml:base=“http://…_SRV/”
xmlns=“http://www.w3.org/2005/Atom”
xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata”
xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices”>
<content type=“application/xml”>
<m:properties>
<d:Id>1</d:Id>
<d:Text>New parent entity</d:Text>
</m:properties>
</content>
</entry>

-changeset_mychangeset1
Content-Type: application/http
Content-Transfer-Encoding: binary

POST $100/HeaderItemNav HTTP/1.1
Content-Type: application/xml
Content-Length: 1021

<?xml version=“1.0” encoding=“utf-8”?>
<entry xml:base=“http://…_SRV/”
xmlns=“http://www.w3.org/2005/Atom”
xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata”
xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices”>
<content type=“application/xml”>
<m:properties>
<d:ZParentId></d:ZParentId>
<d:Id>000010</d:Id>
<d:Text>First child entity</d:Text>
</m:properties>
</content>
</entry>

-changeset_mychangeset1
Content-Type: application/http
Content-Transfer-Encoding: binary

POST $100/HeaderItemNav HTTP/1.1
Content-Type: application/xml
Content-Length: 1021

<?xml version=“1.0” encoding=“utf-8”?>
<entry xml:base=“http://…_SRV/”
xmlns=“http://www.w3.org/2005/Atom”
xmlns:m=“http://schemas.microsoft.com/ado/2007/08/dataservices/metadata”
xmlns:d=“http://schemas.microsoft.com/ado/2007/08/dataservices”>
<content type=“application/xml”>
<m:properties>
<d:ZParentId></d:ZParentId>
<d:Id>000020</d:Id>
<d:Text>Second child entity</d:Text>
</m:properties>
</content>
</entry>

-changeset_mychangeset1-

-batch_mybatch-

 

below is the response:

Error in log: Dereferencing of the NULL reference

I debugged the code and found that it is failing here. This appears to be an issue with format of dataload only: