Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
ChandraMahajan
Active Contributor

Introduction


In this blog I will explain creation of simple SAP NW GW OData service which will implement Create, Read, Update, Delete, Query and Function Import operations.

 

Just to make it very simple, I will just have single entity and entity set. Here I will perform these operations on Z table. In real scenarios, you will use BAPIs. RFCs to perform these operations.

 

I recommend to read below documents and SAP documentation for deep understanding.

How to Develop a Gateway Service using Code based Implementation by andre.fischer

How to Develop Query Options for an OData Service Using Code-Based Implementationby andre.fischer

How to Write an OData Channel Gateway Service. Part 2 - The Runtime Data Provider Class

SAP Help - SAP NetWeaver Gateway Foundation Developer Guide - SAP NetWeaver Gateway Foundation (SAP_GWFND) - SA...

Code Snippet - 3.2 Data Provider Class (DPC) - SAP NetWeaver Gateway Foundation (SAP_GWFND) - SAP Library

 

Scenario


We have User information table as ZUSERINFO containing below fields



Now let's create OData service which will insert, read, delete, update and query this table along with one custom operation (UsersByCountry) as function import.

 

From an ABAPer perspective, This is what meant by OData operations.









































OData Operation HTTP Method What it meant to an ABAPer
Create POST Insert <table> from <workarea>
Read GET Select Single * From <table> into <workarea>
Update PUT/PATCH Update <table> set <workarea>
Delete DELETE Delete from <table>
Query GET Select *  From <table> Into Table
Function Import GET/POST Everything covered by GET and POST. But only use if scenario does not fit into CRUDQ operations.


 

We can correlate Entity Set as Table Type, Internal table and Entity to work area, structure!

 












Entity Set Table Type or Internal Table
Entity Structure or Work Area


 

 

Procedure


 

Let’s go to transaction SEGW and create project as ZUSERINFO.



Now right click on Data Model and Import --> DDIC Structure option, it will display popup window. Provide DDIC structure name. In this case table name ZUSERINFO. It will propose field and key mapping as well as object name which will be your entity name.



We will ignore MANDT as key field and also overwrite object name. I want my field names in upper camel case format so I will change it accordingly.  Let’s call entity type as User. It will look as below. Press enter.



finally our entity type User will look as below.



Now let’s create Entity Set as UserCollection (or UserSet or Users). You can refer Creating High-Quality OData Services - SAP NetWeaver Gateway Foundation (SAP_GWFND) - SAP Library

 

I will go with UserCollection as my entity set name.

 

Right click folder name Entity Sets and click create. Provide entity set name as UserCollection and Entity Type name as User. It will display as below.



Now let’s generate runtime artifacts. Click on generate runtime objects button. It will display popup as below. Keep the default class names as-is and click on enter button.



On successful generation, you will see this kind of message log and generated artifacts. 4 classes will get generated. 2 for Data provider and 2 for Model provider.



Now register your service under service Maintenance folder. Click on Register button. Keep default values as-is and hit enter button.



On successful registration, click Maintain button. This will open service catalog window along with option to call Gateway Client. Click on Gateway Client button to test the service. (you can also call transaction /IWFND/GW_CLIENT to open SAP NW Gateway client)

 

Append $metatda to base service URL and press execute button. If everything is fine then you will HTTP Response as below. Metadata provides information such as Entity type, key property, properties and Entity Set name.



So far we just defined single entity type and entity set. Now it’s time to code CRUDQ and function import methods.

Coding


 

There is no specific order to implement these methods but it is always good to 1st implement query and read operation as for Create and Update, you will need request data which you will get if you already have query/read implemented.


 

1) Query Operation


 

First we will start implementing query operation. Before that, I will add one record in my ZUSERINFO table as



Now open Runtime artifacts folder and right click on Class ZCL_ZUSERINFO_DPC_EXT and select Go to ABAP Workbench option. Select edit mode and redefine method USERCOLLECTION_GET_ENTITYSET.



In the simplest form, this is what minimal coding will look like in GET_ENTITYSET method.


METHOD usercollection_get_entityset.

DATA: lt_userinfo TYPE TABLE OF zuserinfo,

ls_userinfo LIKE LINE OF lt_userinfo,

ls_entity LIKE LINE OF et_entityset.

*Get data from ZUSERINFO table

SELECT * FROM zuserinfo INTO TABLE lt_userinfo.

*Fill ET_ENTITYSET

LOOP AT lt_userinfo INTO ls_userinfo .

ls_entity-userid = ls_userinfo-userid.

ls_entity-firstname = ls_userinfo-firstname.

ls_entity-lastname = ls_userinfo-lastname.

ls_entity-email = ls_userinfo-email.

ls_entity-phone = ls_userinfo-phone.

ls_entity-country = ls_userinfo-country.

APPEND ls_entity TO et_entityset.

ENDLOOP.

ENDMETHOD.


We are selecting all data from table ZUSERINFO and appending the result to exporting parameter ET_ENTITYSET.

 

Now you can go to GW client transaction and execute URI  /sap/opu/odata/sap/ZUSERINFO_SRV/UserCollection which will display one record which we already added into Z table.



Observe the method signature. put external breakpoint in method and execute query. You will find important information in method parameters in debugging mode.

 

Below table will provide brief explanation of method parameters, alternative approach to get the value of those method parameters. Last column specifies if we need to code to implement query operation.

 

Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_ENTITYSET

 
















































OData Query Method Parameter Alternative way to get the value Coding required to implement Query Operation
UserCollection?$filter=UserID eq '123' and LastName eq 'Mahajan' IT_FILTER_SELECT_OPTIONS and IV_FILTER_STRING DATA: my_filter_options TYPE /iwbep/t_mgw_select_option,
my_filter_string
TYPE string.
my_filter_options
= io_tech_request_context->get_filter( )->get_filter_select_options( ).
my_filter_string 
io_tech_request_context->get_filter( )->get_filter_string( ).
Yes
UserCollection?$select=FirstName,LastName No method parameter data: my_select_fields type /iwbep/t_mgw_tech_field_names.
my_select_fields
= io_tech_request_context->get_select( ).
No


UserCollection?$orderby=FirstName,LastName

 

OR

 

UserCollection?$orderby=FirstName desc,LastName desc
IT_ORDER data: my_orderby_fields type /iwbep/t_mgw_tech_order.
my_orderby_fields
= io_tech_request_context->get_orderby( ).
Yes
UserCollection?search='test' IV_SEARCH_STRING data: my_search_string type string.
my_search_string
= io_tech_request_context->get_search_string( ).
Yes
UserCollection?$top=1 IS_PAGING-TOP data: my_top type string.
my_top
io_tech_request_context->get_top( ).data: my_skip type string.
my_skip
io_tech_request_context->get_skip( ).
Yes
In case if we had association and navigation between 2 entities IT_NAVIGATION_PATH DATA: my_nav_path type /iwbep/t_mgw_tech_navi.
my_nav_path
= io_tech_request_context->get_navigation_path( ).
Yes


 

2) Read Operation


 

Now let’s implement GET_ENTITY method. Go to method USERCOLLECTION_GET_ENTITY and redefine it. Below is the minimal code that we need to have in this method.

 

We need to read the key values passed from query URI and then fill the export parameter ER_ENTITY.

METHOD usercollection_get_entity.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_userid TYPE zuserinfo-userid,

ls_userinfo TYPE zuserinfo.

*Get the key property values

READ TABLE it_key_tab WITH KEY name = 'UserID' INTO ls_key_tab.

lv_userid = ls_key_tab-value.

*Get the single record from ZUSERINFO and fill ER_ENTITY

SELECT SINGLE * FROM zuserinfo INTO ls_userinfo WHERE userid = lv_userid.

IF sy-subrc = 0.

er_entity-userid = ls_userinfo-userid.

er_entity-firstname = ls_userinfo-firstname.

er_entity-lastname = ls_userinfo-lastname.

er_entity-email = ls_userinfo-email.

er_entity-phone = ls_userinfo-phone.

er_entity-country = ls_userinfo-country.

ENDIF.

ENDMETHOD.


Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_ENTITY




































OData Query Method Parameter Alternative way to get the value Coding required to implement Query Operation
UserCollection(UserID='Test') OR UserCollection('Test' IT_KEY_TAB DATA: lt_keys TYPE /iwbep/t_mgw_tech_pairs.
lt_keys
= io_tech_request_context->get_keys( ).
Yes
In case if we had association and navigation between 2 entities IT_NAVIGATION_PATH DATA: my_nav_path type /iwbep/t_mgw_tech_navi.
my_nav_path
= io_tech_request_context->get_navigation_path( ).
Yes
UserCollection('Test')?$select=FirstName,LastName No DATA: my_select_fields TYPE /iwbep/t_mgw_tech_field_names.
my_select_fields
= io_tech_request_context->get_select( ).
No
UserCollection('Test')?$format=json No No No


 

Also note that you cannot use system query options else you will get an error as System query options '$orderby,$skip,$top,$skiptoken,$inlinecount,' are not allowed in the requested URI

 

3) Create Operation


 

Now we will focus on create operation by redefining method USERCOLLECTION_CREATE_ENTITY. Below is the code that will perform POST operation.

 

Here we are reading the request data and then filling the exporting parameter ER_ENTITY.

METHOD usercollection_create_entity.

DATA: ls_request_input_data TYPE zcl_zuserinfo_mpc=>ts_user,

ls_userinfo TYPE zuserinfo.

* Read Request Data

io_data_provider->read_entry_data( IMPORTING es_data = ls_request_input_data ).

* Fill workarea to be inserted

ls_userinfo-userid = ls_request_input_data-userid.

ls_userinfo-firstname = ls_request_input_data-firstname.

ls_userinfo-lastname = ls_request_input_data-lastname.

ls_userinfo-email = ls_request_input_data-email.

ls_userinfo-phone = ls_request_input_data-phone.

ls_userinfo-country = ls_request_input_data-country.

* Insert Data in table ZUSERINFO

INSERT zuserinfo FROM ls_userinfo.

IF sy-subrc = 0.

er_entity = ls_request_input_data. "Fill Exporting parameter ER_ENTITY

ENDIF.

ENDMETHOD.


To test POST operation, 1st execute GET operation and then press Use as Request button which will copy the response to request window and then select operation POST and execute.

 

In case you execute GET operation i.e. GET_ENITITYSET and then try to perform POST operation then you will get below kind of error. Hence make sure that you execute GET to read single entity i.e. GET_ENTITY operation and then perform POST.



So correct steps are,

1) Execute GET to read single entity /sap/opu/odata/sap/ZUSERINFO_SRV/UserCollection('Test')

2) Click Use as Request button

3) Update your request properties.

4) Select HTTP method as POST

5) Query as /sap/opu/odata/sap/ZUSERINFO_SRV/UserCollection and execute



 

Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_ENTITY_C

 
















OData Query Method Parameter Coding required to implement Query Operation
/sap/opu/odata/sap/ZUSERINFO_SRV/UserCollection DATA: ls_request_input_data TYPE zcl_zuserinfo_mpc=>ts_user.
io_data_provider
->read_entry_data( IMPORTING es_data = ls_request_input_data ).
Yes


 

In case of Create operation, you cannot use system query options else you will get an error as


The Data Services Request contains SystemQueryOptions that are not allowed for this Request Type

 

This error message comes from method PROCESS_ENTITY_SET (/IWCOR/CL_DS_PROC_DISPATCHER). Just in case if you are curious and want to know :wink:

4) Update Operation


 

Now we will implement update operation by redefining method USERCOLLECTION_UPDATE_ENTITY. We need to put below code in this method.

METHOD usercollection_update_entity.

DATA: ls_request_input_data TYPE zcl_zuserinfo_mpc=>ts_user,

ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_userid TYPE zuserinfo-userid,

ls_userinfo TYPE zuserinfo.

* Get key values

READ TABLE it_key_tab WITH KEY name = 'UserID' INTO ls_key_tab.

lv_userid = ls_key_tab-value.

IF lv_userid IS NOT INITIAL.

* Read request data

io_data_provider->read_entry_data( IMPORTING es_data = ls_request_input_data ).

* Update fields of table ZUSERINFO

UPDATE zuserinfo SET firstname = ls_request_input_data-firstname

lastname = ls_request_input_data-lastname

email = ls_request_input_data-email

phone = ls_request_input_data-phone

country = ls_request_input_data-country

WHERE userid = lv_userid.

IF sy-subrc = 0.

er_entity = ls_request_input_data. "Fill exporting parameter ER_ENTITY

ENDIF.

ENDIF.

ENDMETHOD.


Here also first we need to execute GET operation to read the entity and then copy the response to request using Use as Request button and execute PUT operation after editing required data. Successful HTTP response will look as below with status code as 204.



Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_ENTITY_U

 
























OData Query Method Parameter Alternative way to get the value Coding required to implement Query Operation
UserCollection('Test1') IT_KEY_TAB

DATA: lt_keys TYPE /iwbep/t_mgw_tech_pairs,

     ls_key TYPE /iwbep/s_mgw_tech_pair.

lt_keys = io_tech_request_context->get_keys( ).
Yes
UserCollection('Test1') IO_DATA_PROVIDER io_data_provider->read_entry_data( IMPORTING es_data = ls_request_input_data ). Yes


5) Delete Operation


 

To implement Delete operation, you need to execute DELETE HTTP method for particular key. Below is the code for method USERCOLLECTION_DELETE_ENTITY.

 

Here we are reading key value of the record to be deleted and executing Delete statement.


METHOD usercollection_delete_entity.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_userid TYPE zuserinfo-userid.

* Read key values

READ TABLE it_key_tab INTO ls_key_tab WITH KEY name = 'UserID'.

lv_userid = ls_key_tab-value.

IF lv_userid IS NOT INITIAL.

* Delete record from table ZUSERINFO

DELETE FROM zuserinfo WHERE userid = lv_userid.

ENDIF.

ENDMETHOD.


On successful record deletion, you will see HTTP response code as 204.



Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_ENTITY_D


















OData Query Method Parameter Alternative way to get the value Coding required to implement Query Operation
UserCollection('Test1') IT_KEY_TAB

DATA: lt_keys TYPE /iwbep/t_mgw_tech_pairs,

     ls_key TYPE /iwbep/s_mgw_tech_pair.

lt_keys = io_tech_request_context->get_keys( ).
Yes


6) Function Import 


As per SAP documentation, Function Imports - SAP NetWeaver Gateway Foundation (SAP_GWFND) - SAP Library

The Open Data Protocol (OData) includes standard CRUD (Create, Retrieve, Update, and Delete) operations that map to the HTTP methods POST, GET, PUT/MERGE, and DELETE.


In addition, OData supports further service operations (function imports) that can be invoked by the HTTP methods GET or POST for anything that cannot be mapped to the standard CRUD operations. You can implement such additional service operations in the Service Builder by creating function imports within your data model.


For example, you could create function imports for the following custom operations:




  • Confirm Work Item

  • Check Flight Availability


While it is simple to create new function imports to invoke custom operations, if the operation you want to use can be invoked using a standard CRUD operation, you should not create a function import. That is, you should only create function imports for custom operations that cannot be invoked using a standard operation.



In simple terms, if an operation cannot fit into CRUD scenario then you can perform it by function import.

 

Suppose our ZUSERINFO table looks like below,



And we want to get users by specific country then we can implement function import. Let’s call our function import as UsersByCountry!

Right click on Data model and create function import. Provide proper name. Again refer Creating High-Quality OData Services - SAP NetWeaver Gateway Foundation (SAP_GWFND) - SAP Library



Provide required details such as mentioned below. Remember here we want to return collection of users and hence we selected Return cardinality as 0..n with return entity set and HTTP method type as GET.



Now click on Function Import parameters and create import parameters as shown below. In this case we just want to pass value of country and hence we will have one parameter as Country.



Finally save project, check project consistency and generate runtime objects. To check if everything is fine, in GW client execute service metadata URL as /sap/opu/odata/sap/ZUSERINFO_SRV/$metadata which should show you function import definition in metadata as below,



Now we will implement function import operation. Go to DPC_EXT class and redefine method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~EXECUTE_ACTION.

Put below code to implement function import.


METHOD /iwbep/if_mgw_appl_srv_runtime~execute_action.

DATA: ls_parameter TYPE /iwbep/s_mgw_name_value_pair,

lv_country TYPE string,

lt_userinfo TYPE TABLE OF zuserinfo,

ls_userinfo TYPE zuserinfo,

ls_entity TYPE zcl_zuserinfo_mpc=>ts_user,

lt_entityset TYPE zcl_zuserinfo_mpc=>tt_user.

IF iv_action_name = 'UsersByCountry'. " Check what action is being requested

IF it_parameter IS NOT INITIAL.

* Read Function import parameter value

READ TABLE it_parameter INTO ls_parameter WITH KEY name = 'Country'.

IF sy-subrc = 0.

lv_country = ls_parameter-value.

ENDIF.

IF lv_country IS NOT INITIAL.

SELECT * FROM zuserinfo INTO TABLE lt_userinfo WHERE country = lv_country.

LOOP AT lt_userinfo INTO ls_userinfo .

ls_entity-userid = ls_userinfo-userid.

ls_entity-firstname = ls_userinfo-firstname.

ls_entity-lastname = ls_userinfo-lastname.

ls_entity-email = ls_userinfo-email.

ls_entity-phone = ls_userinfo-phone.

ls_entity-country = ls_userinfo-country.

APPEND ls_entity TO lt_entityset.

ENDLOOP.

* Call methos copy_data_to_ref and export entity set data

copy_data_to_ref( EXPORTING is_data = lt_entityset

CHANGING cr_data = er_data ).

ENDIF.

ENDIF.

ENDIF.

ENDMETHOD.


To test function import we need to query as /sap/opu/odata/sap/ZUSERINFO_SRV/UsersByCountry?Country='US'

 

If there are multiple parameters then it will be separated by an ampersand (&).



Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_FUNC_IMPORT

 


















OData Query Method Parameter Alternative way to get the value Coding required to implement Query Operation
/sap/opu/odata/sap/ZUSERINFO_SRV/UsersByCountry?Country='US' IT_PARAMETER DATA: my_parameter TYPE  /iwbep/t_mgw_name_value_pair.
my_parameter
= io_tech_request_context->get_parameters( ).
Yes


Closing Remarks


This is very simple example of an OData service. In real case scenarios, you will have multiple entities, relationship, association and navigation between them but when it comes to coding in DPC_EXT class methods, you will find above explanation of each method, parameters and alternative way to get the parameter values useful.

I hope you enjoyed reading this blog and now ready to develop your OData service! I request you to put comments/suggestions. Please feel free if you have any different thought to improve any part of this blog as well.

 

[Update - 9/25/2014] I have posted new blog which focuses more on association/navigation and data provider $expand. You can access it at Let's code association/navigation and data provider expand in OData service!


Happy Learning & Coding :smile: :smile: :smile:
125 Comments
Former Member
0 Kudos

Nice documentation  Chandra:)

It's very helpful!

Do you have any links for creating Gateway service for  Stored Procedures

Thansk,

Santhoshi.

Former Member
0 Kudos

@ chandrashekhar.mahajan

Good morning chandrashekhar.mahajan , I just want to say THANK YOU for this USEFUL blog. Very INTERESTING :smile: !

VenkyM
Participant
0 Kudos

Dear Chandra,

Superb blog .. excellent stuff.

Thanks,

Venky

Former Member
0 Kudos

Dear Experts ,

     i am  new to gateway . i am trying to create ODATA service for "BAPI_SALESORDER_CREATEFROMDAT2" . Let me use your knowledge to do this task.

Regards

0 Kudos

Really Nice and an Useful Blog

Former Member
0 Kudos

Hello,

I have attempted following this guide, but when I attempt to access /sap/opu/odata/sap/ZUSERINFO_SRV_01/UserCollection('Test')


I get the error 404: Resource not found for segment 'User'

I have looked into the get_entity method but can not find where this problem comes from.

I have copy pasted the METHOD usercollection_get_entity.


I hope that someone can help

0 Kudos
Hi Ajdin

Check your Read statement in DPC_EXT class, the key name you're passing is case sensitive. Kindly cross check your key name with entity type properties.
Former Member
0 Kudos
Hi,

Can we insert multiple records into a table at a time, using CREATE_ENTITY operation in OData ??
0 Kudos
My first successful post method after following your post.

Thank you.
It would be helpful if you can input data from sapui5 front end application and update database using Odata.
dilna_tp
Explorer
0 Kudos
THANK YOU SO MUCH!!
ana_oliveira
Discoverer
0 Kudos
Than you for the post, it is really helpful. I have one error when executing HTTP methods PUT or POST (any other than GET). Do you know how this can be fixed? Thank you. 
former_member335011
Discoverer
0 Kudos
Thank you for  this good example.
praveenkrishp
Discoverer
0 Kudos
Hi Chandra,

Your article is very much useful to get a clear idea about CRUD operations and how to do it.

Thanks a lot.!

 
Former Member
0 Kudos
Hi Chandra ,

 

Do you have any examples of passing filter when using Expand .
0 Kudos
Nice guide tutorial, thank you
former_member548272
Participant
0 Kudos
Hi,

 

Thanks.

I have a question.

How do you send a table as input in POST and GET operation ?

Until now, all tutorials are sending string or structure. I want to create an entry using a table send to my RFC.

 

Thank you.

 

Regards.
arafat_shaik
Explorer
You should use the method CREATE_DEEP_ENTITY for this kind of requirement. I guess it's too late to answer this question, nonetheless someone will find it helpful..:)
raschmo
Explorer
0 Kudos

Thank you. This was very useful to get started.

pavan888
Discoverer
0 Kudos
Thanks for the example it is very helpfull
prakashmani
Explorer
0 Kudos
Very good explanation on query operations and parameters to be used.
0 Kudos
Hi,

It's a great blog.

Question - Could you please explain on how checking the eligibility not be done by crudq and only using function import?

Is it the request or response that make function important different from the get/post entity/entityset?

 

 
Jelena
Active Contributor

I realize this blog post is from 2014 but it comes up on top of various Google searches for function import implementation. There is an important error in the blog text that caused me to waste a lot of time and some hair.

Regarding function import, the post says "If there are multiple parameters then it will be separated by comma". This is incorrect. If you do that, you will get a vague error message "Invalid Function Import Parameter" and will go down the spiral of madness trying to figure out where you made a mistake. (And oh, it's super easy to make a typo in these requests.)

In a function call, multiple parameters need to be separated by an ampersand (&), not a comma. This is a bit weird since usually it separates different commands but it just worked for me.

So, in this example, if the function import had, say, a second parameter called Something, then the URI would look like this:

/sap/opu/odata/sap/ZUSERINFO_SRV/UsersByCountry?Country=’US’&Something='Something'

 

 

ChandraMahajan
Active Contributor
Thanks Jelena. I made the correction. FYI, I wrote this blog in 2014. Thanks!
Jelena
Active Contributor
0 Kudos

Thanks for doing this! Also thanks for correcting my date typo! 🙂

Labels in this area