0% found this document useful (0 votes)
2 views95 pages

Odata Note Part3

This document provides a comprehensive guide to OData, detailing its fundamentals, including HTTP basics, REST architecture, and the differences between URIs and URLs. It explains OData as an open data access protocol that facilitates the creation and consumption of RESTful APIs, highlighting its advantages and the role of SAP Netweaver Gateway in connecting enterprise data. The document also outlines deployment options for SAP Netweaver Gateway and discusses the tools available for modeling OData services.

Uploaded by

Shakti Chandan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views95 pages

Odata Note Part3

This document provides a comprehensive guide to OData, detailing its fundamentals, including HTTP basics, REST architecture, and the differences between URIs and URLs. It explains OData as an open data access protocol that facilitates the creation and consumption of RESTful APIs, highlighting its advantages and the role of SAP Netweaver Gateway in connecting enterprise data. The document also outlines deployment options for SAP Netweaver Gateway and discusses the tools available for modeling OData services.

Uploaded by

Shakti Chandan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 95

4 OData – Everything that you need to know

Introduction
This series of blogs is an attempt to provide a comprehensive guide to OData and how it is consumed in the
SAP world. There has been a lot of content on the Internet explaining what is OData, how the services and
published and how to consume which is scattered and assumes that the reader has clarity already on some
very important fundamentals. However, there are so many aspects which deserves more than just a mention.
In this blog, we will try and cover the topic 360 degrees.

We start with the very basics of HTTP and then gradually build on to OData and finally conclude with a detailed
end-to-end exercise on how to create an OData service and how to consume it.

How Internet Works?


▪​ It might sound to be an unrelated topic when the focus is on OData. HTTP is the common thread
between the two. HTTP is the underlying protocol on which OData is based and also one of the most
common language which is spoken on the Internet.

▪​ You would typically need a Web Browser, address of the document you want to read or the URL
(Uniform Resource Locator), a Web Server running at the system where the document is located.
▪​ Web Browser is a software which is responsible to retrieving and presenting resources on the web.
Web Browser parses the HTML response received from the Web and prepares a DOM Tree. DOM
stands for Document Object Model and it is a language-independent convention of representing objects
in HTML. Web Browser acts as an interface between you and the World Wide Web.
▪​ You can request for a document located somewhere on this planet by providing its address via the
browser which sends HTTP requests to the web server. The Web server then sends back HTTP
response that is parsed by the Web Browser for you to view the document on your machine.
▪​ HTTP stands for Hyper Text Transfer Protocol and as the name suggest it defines the semantics of
what the browser and the web server can communicate to each other. The how part of the
communication or the byte by byte transfer of the data packets across the network is not HTTP but it is
done via TCP/IP protocols.
▪​ An important component in the story so far is also the Web Server. In a nutshell, Web Server is a server
program which sits on a physical server and waits for requests from a client. Whenever a request is
received the Web Server prepares a response and sends it back to the client using HTTP protocol. It is
important to note that HTTP is not the only but, by far most commonly used protocol and that the client
here can be a browser or any other software that communicates in HTTP.

Let us now try and put up a formal definition to the HTTP.

“HTTP is based on a Client-Server architecture style and it uses a stateless request/response protocol to
communicate between the client and the web server. The HTTP protocol helps define what requests can be
sent across by the client and response sent by web server. The communication of data over the network is
however done using on TCP/IP protocol”.

It is evident that HTTP is based on client server architecture from our description till now, browser being the
client which sends HTTP request and web server which sends the response back to the browser. But, why
stateless?

Every single HTTP request that is received by the web server is forgotten after a response has been sent
across. Web servers do not process an HTTP request by remembering the previous request.

URI and URL are not the same thing


We mentioned a while back that you need to know the URL of the document you wish to view in your browser.
Can I not mention a URI as an address to get the document on the web? Let us quickly understand the
difference between the two.

URI or the Uniform Resource Identifier can be classified as a name, locator or both that identifies a resource
uniquely.

URL (Uniform Resource Locator) is actually a subset of URI that not only identifies a resource uniquely but
also provide means of locating the resource.

Defining the Request and Response


HTTP requests are sent with one of the defined Request Methods that indicates the action to be taken on the
resource. Following are the two most commonly used methods:

GET

This is used to retrieve information about the resource from the server.

POST

This method is used to send data to the server.


For example, if you enter a text string on Google search page and press enter, it will generate an HTTP
Request with GET method in the background. On the other hand, if you provide your username/password on a
Login page and press enter a POST HTTP request will be send to the server.

HTTP Response from the Web Server comes with data and the status code. The status code provides a
context to the response. For instance, if you do not correctly provide the resource location, the web browser
will send you a response which you are not expecting. Along with the data response comes the status code,
known universally in the HTTP world, that explains to the user what could be the reason for an unexpected
response.

HTTP codes are 3 digit integers.

Code and Description


1xx: Informational

It means the request has been received and the process is continuing.
2xx: Success

It means the action was successfully received, understood, and accepted.


3xx: Redirection

It means further action must be taken in order to complete the request.


4xx: Client Error

It means the request contains incorrect syntax or cannot be fulfilled.


5xx: Server Error

It means the server failed to fulfill an apparently valid request.

For example, status code 400 means Bad Request. It means that the server did not understand the request.

OData allows creation and consumption of RESTful APIs. In the next blog, I will cover what are RESTful APIs
as the next stepping stone towards understanding OData.

What is REST?
REST or Representational State Transfer is an architectural style that uses simple and lightweight mechanism
for inter-machine communication. It is an alternative to the RPC (Remote Procedure Calls) and Web Services.

REST is resource based unlike RPC or SOAP which are action based. In SOAP you will have a request to get
material data whereas in REST, material will be identified as a resource using URI and then use HTTP verbs to
determine which operation shall be performed on the resource. It is important to note here that it is possible to
have multiple URIs pointing to the same resource.
Representation of the resource is not the resource itself but only a representation.
The term representation is how the resources are manipulated in a REST based architecture
Representations depict parts of the resource state which are transferred between client and server in mostly
JSON or XML format. Client will typically have enough information to manipulate the resource on the server.
For example, if Person is modelled as a resource and there is a service to get contact information of a person
then the representation that you will get of that Person would be Name, Address and Phone details in JSON or
XML format.

The Six Constraints


Following are the six constraints defined which are attributes of a RESTful API.

1. Uniform Interface

Uniform Interface is fundamental to the design of any REST based service. The uniform interface simplifies
and decouples the architecture, which enables each part to evolve independently.
What that means is that it should be possible to identify the individual resource in the request, for example
using URI. Once the Client has a representation of the Resource then, it should have enough information to
update or delete the resource. And that the Client should not assume that any particular action is available on
a resource beyond those described in the representation received from the server previously.

2. Stateless

The Server should not contain any Client state. It is possible to process a request only if they are
self-descriptive and that the request should have enough context to act upon. For example, if the Person
resource address needs to be updated then it is required that the Client pass on the particular Person resource
details in the request for which it has received the representation from the Server in the previous request. If the
state has to be maintained, it should be at the Client side.
3. Client-Server

We have already established this constraint that the RESTful architecture is a Client-Server architecture.
Resource representations are transferred between client and server. We should always keep at the back of our
minds that the Client of RESTful APIs will not have direct connects to the Resources.

4. Cacheable

Another constraint on a RESTful API is that any response that is coming from the Sever should be cacheable
on the Client side. Caching could be implicit, explicit or negotiable. Implicit and Explicit caching is
self-explanatory whereas Negotiable caching means that the Server and Client agree on how long a
representation can be cached on the Client.

5. Layered System

The constraint of Layered System is closely related to the above two constraints of Client-Server and
Cacheable. It suggests that the Client of a RESTful API should not assume that there will be a direct
connection between the client and the server. There could be multiple layers of software or/and hardware in
between the two. The Client need not know whom exactly it is talking to and whether a response is coming
from the server or is accessed from a local cache. This improves scalability.

6. Code on Demand

This constraints suggests that it should be possible that a Server can extend a Client temporarily. It means that
Server can transfer logic to the client as representation to be executed at the client.
This is the only optional constraint.
A RESTful service needs to adhere to all of the above mentioned constraints (except Code on Demand) to be
called as a RESTful API.

What is OData?
“Open Data Protocol (OData) is an open data access protocol from Microsoft that allows the creation and
consumption of query-able and interoperable RESTful APIs in a simple and standard way”.

The protocol enables the Clients to publish and manipulate the resource identified by URIs and defined in a
data model using simple HTTP messages.

To put it in simple words, OData is an open source to exchange data over the Internet. Server hosts the data
and clients can call this service to retrieve the resources and manipulate them. Servers expose one or more
endpoints which are services that refer to the resources. Clients need to know this server side endpoint to call
the service to query or manipulate the data. The protocol is HTTP based and designed with a RESTful mindset
which means it follows the constraints to be called as a RESTful service.

Since the protocol is HTTP based, any programming language with HTTP stack can be used to consume
OData services. Existing Client side libraries can be used to transform the JSON or ATOM payloads from the
server into objects making programming simple. On the other hand, many libraries exist on the Server side to
generate the payloads in ATOM or JSON from the existing data.

It is important to note that both Client side and Server side development can be in completely different
programming languages till the time both are able to communicate via HTTP.

Clients consume the service to query and manipulate the data from OData Services and are also called as
Consumers of OData Service.
Similarly, Servers that expose the OData services via endpoints are known as Producers of Odata services.

So, we now know that in OData protocol, the resources are exposed in two formats; XML based Atom and
JSON.

A brief description of Atom and JSON.

Atom is a combination of two protocols, Atom Syndication and Atom Publishing protocol. The Atom
Syndication Format is an XML language used for web feeds, while the Atom Publishing Protocol (AtomPub or
APP) is a simple HTTP-based protocol for creating and updating web resources.

JSON stands for JavaScript Object Notation is a lightweight data-interchange format. JSON is self-descriptive
and easy to use and is completely language-independent.

Advantages with OData


There is a lot of data on the web today but lot of it is locked up in different specific applications or formats and
different to access from outside. Many Organizations have now started exposing data using REST based
services however, it is difficult to write applications which works with multiple data sources as each provider
will expose the data in a slightly different way. OData service producers can expose its service along with
metadata which contains the semantics for consumption. OData exploits the common formats like XML, Atom
and JSON for communication which are commonly understood. Clients can now understand these OData
services using generic tools and can combine information from multiple data sources.

Exposing your data with OData services comes with multifold advantages. For example, as we mentioned
earlier, as a consumer, you need not worry about the programming language used by the producer as long as
the services are exposed as OData service.
ODBC for the Web
ODBC (Open Database Connectivity) is a standard API to access the database management systems
independent of the database management systems or operating systems. ODBC manages this by adding
drivers between the Application layer and the DBMS to translate the queries fired by application into
instructions which DBMS can understand.

OData has similarity with the ODBC in a sense that here, OData provides the middleware between producers
and consumers to communicate data. There is a uniform way to consume data and is independent of the
producer much like ODBC. The fact that OData is based on HTTP RESTful services makes it the ODBC for
the Web!

SAP Netweaver Gateway – An Introduction


For Business Users, ease of access has become critical with the advent of various mobile devices that support
end-to-end scenarios and also provides a rich user experience. There is a need to better integrate data,
reduce complexity and improve productivity. This has been a challenge for the Developer community to be
able to support multiple platforms to consume enterprise data more easily than ever before and on the other
hand provide solutions that are scalable and with low TCO (Total Cost of Ownership).

Point-to-point solutions are not the right way as it has a lot of drawbacks including duplication of development
effort, increased cost, complex landscape, poor scalability and difficulty to maintain.

SAP Netweaver Gateway is a technology that has the answer to this. It can seamlessly connect devices,
platforms and environments to SAP Enterprise Data using the OData services. SAP Netweaver Gateway offers
connectivity to SAP Business data using any programming language and without the need of strong SAP
development knowledge.
SAP Netweaver Gateway comes with design-time tools to facilitate modeling OData services for consumption.
These tools improve user experience and provide automatic connectivity to the SAP backend that reduces
development efforts and improves productivity.

SAP Netweaver Gateway sits on top of the existing SAP Backend infrastructure. There are set of add-on
components that needs to be installed on a SAP Backend system to enable SAP Netweaver Gateway
services. It is this architecture that enables consumption of SAP data by variety of interfaces such as BAPIs
and RFCs
SAP Netweaver Gateway Service Builder tools is SAP GUI based modeler to create and publish Odata
services using transaction SEGW.

There is also an Eclipse-based Odata Modeler tool available. It provides an easier way to model the service
that is a much easier way to develop if you are not too familiar with ABAP programming language. The models
can be conveniently created here are exported to the development environment for the Gateway.

SAP Annotation with Odata

SAP has leveraged the extensibility feature provided by Atom Publishing protocol to add SAP specific
annotations. AtomPub allows you to add your own markups in the service document. SAP adds annotations
from the ABAP Data Dictionary, for example, labels to the documents which can then be consumed by the
front end application without having to worry about translations in this case.

In my next blog, I will focus on the various deployment options for SAP Netweaver Gateway.

SAP Netweaver Gateway prior to NW 7.4 had three add-ons namely GW_CORE, IN_FND and IW_BEP. While
the first two components were required for Gateway server functionalities, IW_BEP was used for Gateway
backend functionalities.

From the onset of NW 7.0 release, all the three components are bundled into a single component
SAP_GWFND or Gateway Foundation.

There are three possible deployment options to pick from and we will discuss each one of them.

1. Hub Deployment: Development in the Backend system

In this deployment strategy, the SAP Netweaver Gateway is installed on a separate SAP machine referred to
as Gateway Hub. The OData services are registered and exposed from the Gateway Hub but are developed in
the SAP backend system. If SAP Business Suite backend systems are running on NW release prior to 7.4 then
component IW_BEP should be installed. For systems running on NW7.4 onwards the SAP_GWFND
component contains both Gateway server and backend functionalities as mentioned before.

Advantages of this deployment options are as follows:

●​ There is only a single access point of access to the SAP Backend systems. No direct access from the
outside world provides enhanced security.
●​
●​ Gateway Hub can be a system running on newer release NW 7.31 or NW 7.4 with a Backed system
running on lower release is perfectly acceptable.
o​ Gateway on newer releases would mean support for SAP UI5.
o​ Supports added authentication features.
●​ Direct access to metadata (DDIC) and business data for OData Modeling by backend systems.
2. Hub Deployment: Development in the Hub

Hub Deployment with development in the Gateway Hub is an option where just like the previous option, there
is a dedicated Gateway Hub as a separate system from the backend system. Since, all the development
related to SAP Netweaver Gateway takes place in the Gateway Hub, backend not necessary should have any
Gateway components installed. It is a feasible option if you don’t want to do any kind of development in the
backend system and leverage what is already available.

There are a few disadvantages though:

●​ Access to the data source for Odata service development is only limited to existing BAPIs and RFCs.
●​
●​ There will be no direct access to the backed dictionary objects to the Gateway Hub where the Odata
services are modeled. The access is limited to only remote access.
●​
●​ Having a landscape with a dedicated system as Gateway Hub involves additional costs as compared to
an embedded deployment option. This is true for the above deployment option as well.

3. Embedded Deployment
In the Embedded Deployment option, the SAP Netweaver gateway components are installed as add-ons on
the SAP backed system itself. Both the Odata modeling and the exposing of the services is done from the
backend system. This deployment strategy saves cost as there is no dedicated Gateway Hub. Also, the
runtime overhead due to remote calls in the above two deployment options is reduced.

Disadvantages:

●​ If there are multiple SAP Business Suite Backed systems in the landscape then, each system will have
its own installation and configuration of Netweaver Gateway components.
●​
●​ Upgrade of backed systems will follow a different cycle than that of Netweaver Gateway.
●​
●​ Additional security measures needed as there is no single access point from the backend system to the
consumers.

We will define the problem statement and give you a glimpse of what we are trying to build as a small
application to showcase the power of OData. We will also build a simple Fiori-like app as a frontend consumer.
Knowledge of Fiori is not at all a must here. As I mentioned earlier, during the course of the end-to-end
exercise we will also explain various artifacts of OData.

In the next few blogs we will be creating a simple Master-Detail Fiori-like app based on SAP Flight Model
example. . We will leverage the existing backend developments to expose the data as OData service that will
be consumed by the frontend. Future blogs will follow the Embedded deployment environment but, most of the
steps we do here are applicable to the other two deployments. It is just a matter of where you do the modeling.

Here, is a link to the Overview of Fiori in case you wish to know more.

The app we are going to develop is a very simple app based on the SFLIGHT example to check seat
availability and add new flight tours.

Master-detail app will have the left navigation panel and will feature the list of Airlines along with codes. The
detail section of the app will contain the tabular list of different flights corresponding Airline. Users will be
allowed to select a flight and check for availability for a given date and class. App will also allow users to add
new flights for a specific Airline and dates on which the flights are scheduled. We will showcase the CRUDQ
operations along with navigations from master to object view and function imports during the course of this
app.

Data Model for SFLIGHT that we will use.


UI Mock-ups for the Sflight App:

Master-Detail view with Airlines information on left navigation panel and various flights operating for a specific
airlines on the details section.
Check Availability scenario

Information on seats availability


Adding a new Flight tour for future dates.

Step by Step process to create Odata services in SAP / SAP HANA system

Step 1. Go to transaction code – SEGW .

Click on Icon Create. A pop window will appear , Fill the details as per below mentioned in screen shot and click on
check icon or enter.
Below screen will appear where you can see below folder in project. In Folder data model, we can see three sub
folders. Entity Type – it acts as work area , Entity Sets -It act as internal table and associations.

Now we are going to define the structure of the work area and internal table. Right click on the Data model, select
import and select DDIC structure .
Here give the details of structure and structure name as per the screen below.

Select radio button Entity type and and click on checkbox entity set. fill ABAP structure as VBAK and click on next

Now you will get the pop up screen with VBAK table fields name. Select fields for your structure and click on next.
project5

Another window will appear where we have to select the key field. tick on Vbeln as the key field and click on finish.
You may get a warning message , it can be totally ignored.

Now expand folder entity type and fill the below parameters in properties , Please don’t tick the Null check box for
VBELN as it is key field.
Similarly Create Structure for SD Item following same steps
click on Save button at the top of menu bar

Now click on generate Icon, A pop up window will appear with class details , click on tick icon and proceed further. It
will ask for a package , give details and proceed further . These are also known as runtime artifacts.

Step 2. Go to transaction code /IWFND/MAINT_SERVICE .

Click on push button Add services .


Next screen will appear, give the alias name and execute , Search you for your project .

Now double click on your project name . A pop up window will appear , enter the package details and click on the
tick icon. An Information message will be shown where it will confirm about the service is created and metadata
loaded successfully
Now click back and go to the main screen of transaction /IWFND/MAINT_SERVICE and find your service.

Click on SAP Gateway Client

A new screen will come, Execute the transaction and check the response .

Now we will write code to get data.

Step 3. Go to transaction code SEGW and expand folder service Implementation .

Under service implementation – Expand SDheaderSet. you will find different options. Right click on GetEntitySet
and select GO to ABAP workBench . It will redirect to the class
Now expand the method drop down and select the method for GetEntitySet for Header data and right click and
select redefine.

Write below code in method. Save and activate all related objects for classes.
Similarly , Write code for GetEntitySet for Item and activate .

Select entity for header and enter

Click and execute and you will get the header data in response
Similarly, Select entity set for item and execute . You will get data for item

Here , We had completed our Odata service creation with a Service Builder Project with two entities

and two entity-sets. We have generated the runtime artifacts and registered and activated our OData service.

We had redefined methods of header and item entity set , load metadata and test the entity set with for test data.

Example-yHow to Develop a Gateway Service using Code based Implementation


Task 1: Creating a Data Model with entity type Product and the entity set Products

1.​ Create a new project ZGW_PRODUCT.


2.​ Import DDIC Structure BAPI_EPM_PRODUCT_HEADER to create the entity type – Product
3.​ Create an entity set Products which is based on the entity type Product you have created before.

Task 2: Generate runtime objects and register and activate the service
1.​ Generate the runtime objects of your project
2.​ Register the service in the Gateway hub system (which is in this case the local system since we are using
Gateway in an embedded deployment scenario)
3.​ Test the service using the Gateway Client.

Task 3: Perform Service Implementation for GET_ENTITYSET

1.​ Implement the method PRODUCTS_GET_ENTITYSET in the extension class of the data provider class
2.​ We will use the function module 'BAPI_EPM_PRODUCT_GET_LIST' to retrieve the product data
3.​ Please note that the parameter ET_ENTITYSET that contains the data returned by the
GET_ENTITYSET method has the same structure as the structure BAPI_EPM_PRODUCT_HEADER
that has been used to generate the entity type Product.

Task 4: Perform Service Implementation for GET_ENTITY

1.​ Implement the method PRODUCTS_GET_ENTITY in the extension class of the data provider class
2.​ We will use the function module 'BAPI_EPM_PRODUCT_GET_DETAIL' to retrieve the product details
3.​ Please note that the parameter ER_ENTITY that contains the data returned by the GET_ENTITY
method has the same structure as the structure BAPI_EPM_PRODUCT_HEADER that has been used to
generate the entity type Product
Task 1: Creating a Data Model with entity Product and the entity set Products

Step -4

Go to transaction /IWFND/MAINT_SERVICE and find out you service , select your service and click on load
metadata.
Information messages will pop up – Metadata has been loaded successfully.

Now Click on SAP Gateway Client .

Now Click on Entity set.

Step: Create a new project in the Service Builder (transaction SEGW)


1. Create a new project ZGW_PRODUCT
Step: Import DDIC Structure to create the entity - Product

1.​ Right-click Data Model and choose Import DDIC Structure

2.​ Enter the following value in the wizard and then press Enter:

ABAP Structure:​ BAPI_EPM_PRODUCT_HEADER

and press ENTER.


Result:
The wizard will automatically fill the field ObjectName with the value BapiEpmProductHeader and
create an entity set based on the DDIC structure.

3.​ Change the default values such that

a.​ the property PRODUCT_ID becomes a key field


b.​ the name of the entity type is Product rather than BapiEpmProductHeader
4.​ As a result the values should like follows.

5.​ Press continue​ .


6.​ Press Save.

Step: Create an entity set for the previously created entity


1.​ Expand the node Data Model and double-click Entity Sets:

2.​ Click the Create button to a new line to the table:

3.​ Enter the following values:

Name Entity Type Name Addressable


Products Product X

4.​ Choose Save:

Caution:

Please make sure that the Addressable flag is marked because otherwise GWPA will refuse to generate a consumer application.

Task 2: Generate runtime objects and register and activate the service

Step: Generate runtime objects


1.​ Choose the Generate pushbutton:

2.​ Leave the default values and choose Enter:


Please note the Technical Service Name ZGW_PRODUCT_SRV is equal to the External Service

Name required to consume this service later on.

3.​ Choose Local Object:

4.​ Verify that the runtime objects have been generated successfully:

The following objects have been generated:

Step: Register and activate the service

1.​ Double-click Service Maintenance:


2.​ Select system GW_HUB and choose the Register button:

Alternatively you can click on the entry GW_HUB on the left hand side, right click and select Register
from the context menu.

3.​ Confirm the warning message displayed in the popup:

4.​ (Optional) If you have defined multiple system alias entries in the Gateway hub system that point back to
your backend press F4 to select a system alias and confirm the Select System Alias popup:

5.​ In the Add Service dialogue leave the default values and enter $tmp as the package and choose
Enter or press the button Local Object:
The External Service Name ZGW_PRODUCT_SRV is defaulted with the Technical Service Name
from the previous step.

6.​ Verify that the service has been registered and activated successfully:

Step: Test the new OData service (metadata only)


1.​ Right click the system in which you have registered the service and select Maintain. Alternatively you can
select the system in the mass maintenance view and select the Maintain button.

2.​ This will start the Activate and Maintain Services transaction /IWFND_MAINT_SERVICE where the
service you have just registered is selected.

3.​ Press the button Gateway Client. This will open in a new window start transaction
/IWFND/GW_CLIENT using the the URI

/sap/opu/odata/sap/ZGW_PRODUCT_SRV/?$format=xml and choose Execute. This will show the


service document of the newly created service.
Please note that ZGW_PRODUCT_SRV is the External Service Name that was registered before.

Task 3: Service Implementation for GET_ENTITYSET

Step: Implement the data provider class


1.​ In the Service Builder go back using the green arrow and expand the node Service Implementation, then
the Entity Set Products and right click on the entry GetEntitySet (Query).

2.​ Confirm the warning that the method PRODUCTS_GET_ENTITYSET has not yet been
implemented.

This will open the Class Builder.

Alternatively expand the node Runtime Artifacts and double-click on


ZCL_ZGW_PRODUCT_DPC_EXT

3.​ Activate the Change mode

4.​ Redefine method PRODUCTS_GET_ENTITYSET.


5.​ Implement Method PRODUCTCOLLECTIO_GET_ENTITYSET
a.​ You may copy the code from
ZCL_ZGW_PRODUCT_DPC_EXT-> PRODUCTS_GET_ENTITYSET or
b.​ Perform the following steps:
i.​ Choose Pattern
ii.​ Select Call Function and enter the value BAPI_EPM_PRODUCT_GET_LIST
iii.​ Assign et_entityset to headerdata

c.​ Or take the code from here using Cut & Paste
d.​ Activate the coding

method PRODUCTS_GET_ENTITYSET.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_LIST'


*​ EXPORTING
*​ MAX_ROWS​ =
TABLES
HEADERDATA​ = et_entityset
*​ SELPARAMPRODUCTID​ =
*​ SELPARAMSUPPLIERNAMES​ =
*​ SELPARAMCATEGORIES​ =
*​ RETURN​ =
.

endmethod.

Start the Gateway Client (Transaction /IWFND/GW_CLIENT) in a separate window to run the service.
Provide the following URI to get the Product Collection:
/sap/opu/odata/sap/ZGW_PRODUCT_SRV/Products
Task 4 Service Implementation for GET_ENTITY

Prerequisites

-​ You have performed Task 1 to 3 and you have performed the following steps
o​ Project creation
o​ You have created a model with the entity set Products which is based on an entity type
Product that has been created using import of the DDIC structure
BAPI_EPM_PRODUCT_HEADER
o​ Implementation of the GET_ENTITYSET method

Objectives

In the following you will learn how to implement the GET_ENTITY method. For this we have to learn how to
retrieve the key(s )

If in the Gateway Client following GET statement is issued

/sap/opu/odata/sap/ZGW_PRODUCT_SRV/Products('AD-1000')

the framework takes care that the method PRODUCTS_GET_ENTITY of the data provider extension class
ZCL_ZGW_PRODUCT_DPC_EXT is called.

In order to provide the caller with the requested content the key value 'AD-1000' shall be retrieved using the
import parameter IO_TECH_REQUEST_CONTEXT.
This import parameter “speaks” the technical (internal) names and hence is not influenced by any
redefined function which could result that the external name of the key field can potentially be changed.
The internal names - unless explicitly set by the model provider - are derived from the "original" name.

We can set an external breakpoint in the PRODUCTS_GET_ENTITY method to check that the key value
‘AD-1000’ can be retrieved in the ABAP code as a key-value pair from the internal table. This table would
contain several entries in case the entity set has more than one key field.

The parameter ER_ENTITY which is used to return the data has the same type as the data dictionary object
that has been imported namely BAPI_EPM_PRODUCT_HEADER.

Step: Implement the method GET_ENTITY operation of the entityset Products


1.​ Open the Service Builder project ZGW_PRODUCT that you have created in the previous exercise.
2.​ Expand the node Service Implementation, drill down to the entity set Products and expand the same.
Right click on the GetEntity method and select Go to ABAP Workbench

3.​ Confirm the message “Operation PRODUCTS_GET_ENTITY has not yet been implemented”

4.​ Scroll down to the method PRODUCTS_GET_ENTITY (1), switch to the edit mode (2) and choose redefine
(3)

5.​ You can retrieve the sample code from the following code snippet.

method PRODUCTS_GET_ENTITY.

DATA:​ lt_keys TYPE /IWBEP/T_MGW_TECH_PAIRS,


ls_key TYPE /IWBEP/S_MGW_TECH_PAIR,
ls_product_id type BAPI_EPM_PRODUCT_ID.
lt_keys = IO_TECH_REQUEST_CONTEXT->GET_KEYS( ).

READ TABLE lt_keys with key name = 'PRODUCT_ID'


INTO ls_key.
ls_product_id-PRODUCT_ID = ls_key-value.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'


EXPORTING
PRODUCT_ID​ = ls_product_id
IMPORTING
HEADERDATA​ = ER_ENTITY
*​ TABLES
*​ RETURN​ = lt_return
.

endmethod.

6.​(Optional) You can check how the data is retrieved in the ABAP code if you set an external breakpoint

Step: Test GET_ENTITY method


1. Check the implementation by using the following request URI in the Gateway Client
CRUDQ and Function Import operations in OData service
We have User information table as ZUSERINFO containing below fields

Now let’s create an 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 is meant by OData operations.


OData HTTP Method What it meant to an ABAPer
Operation

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 GET/POST Everything covered by GET and POST. But only use if scenario does not fit into
Import 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 a project as ZUSERINFO.

Now right click on Data Model and Import –> DDIC Structure option, it will display a 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 the generate runtime objects button. It will display a 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 the 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 an 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 $metadata 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 operations. Before that, I will add one record in my ZUSERINFO table as

Now open the 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 IT_FILTER_SELECT_OPTIO DATA: my_filter_options TYPE /iwbep/t_mgw Yes


eq ‘123’ and LastName eq NS and IV_FILTER_STRING _select_option,​
‘Mahajan’ my_filter_string TYPE string.​
OData Query Method Parameter Alternative way to get the value Coding required to implement
Query Operation

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( ).

UserCollection?$select=FirstN No method parameter data: my_select_fields type /iwbep/t_mgw_te No


ame,LastName ch_field_names.​
my_select_fields = io_tech_request_context-
>get_select( ).

UserCollection?$orderby=First IT_ORDER data: my_orderby_fields type /iwbep/t_mgw_t Yes


Name,LastName ech_order.​
my_orderby_fields = io_tech_request_contex
t->get_orderby( ).

OR

UserCollection?$orderby=First
Name desc,LastName desc
UserCollection?search=’test’ IV_SEARCH_STRING data: my_search_string type string.​ Yes
my_search_string = io_tech_request_context
->get_search_string( ).

UserCollection?$top=1 IS_PAGING-TOP data: my_top type string.​ Yes


my_top = io_tech_request_context->get_top
( ).

data: my_skip type string.​


my_skip = io_tech_request_context->get_ski
p( ).
In case if we had association IT_NAVIGATION_PATH DATA: my_nav_path type /iwbep/t_mgw_tech Yes
and navigation between 2 _navi.​
entities my_nav_path = io_tech_request_context->ge
t_navigation_path( ).

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 IT_KEY_TAB DATA: lt_keys TYPE /iwbep/t_mgw_tech_pair Yes


UserCollection(‘Test’ s.​
lt_keys = io_tech_request_context->get_keys
( ).

In case if we had association and IT_NAVIGATION_PAT DATA: my_nav_path type /iwbep/t_mgw_tech Yes
navigation between 2 entities H _navi.​
my_nav_path = io_tech_request_context->ge
t_navigation_path( ).

UserCollection(‘Test’)?$select=FirstN No DATA: my_select_fields TYPE /iwbep/t_mgw No


ame,LastName _tech_field_names.​
my_select_fields = io_tech_request_context-
>get_select( ).

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/UserCollectio DATA: ls_request_input_data TYPE zcl_zuserinfo_mpc=>ts_user.​ Yes


n io_data_provider->read_entry_data( IMPORTING es_data = ls_req
uest_input_data ).

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 the method PROCESS_ENTITY_SET (/IWCOR/CL_DS_PROC_DISPATCHER). Just in case if you are

curious and want to know

4) Update Operation

Now we will implement the 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, Yes

ls_key TYPE /iwbep/s_mgw_tech_pair.

lt_keys = io_tech_request_context->get_keys( ).
UserCollection(‘Test1’) IO_DATA_PROVIDER io_data_provider->read_entry_data( IMPORTING es_data = ls_req Yes
uest_input_data ).

5) Delete Operation

To implement the Delete operation, you need to execute the DELETE HTTP method for a particular key. Below is the code for the
method USERCOLLECTION_DELETE_ENTITY.
Here we are reading the key value of the record to be deleted and executing the 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 Alternative way to get the Coding required to


Parameter value implement Query
Operation

UserCollection(‘Test1’) IT_KEY_TAB DATA: lt_keys TYPE Yes


/iwbep/t_mgw_tech_pairs,

ls_key TYPE
/iwbep/s_mgw_tech_pair.

lt_keys =
io_tech_request_context->get_keys
( ).
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 comma.

Here IO_TECH_REQUEST_CONTEXT refers to /IWBEP/IF_MGW_REQ_FUNC_IMPORT

ODATA service and implement CRUD methods Example-2


Go to SEGW to create a project: ZEMP_MODEL_ODATA using the create project button.
Right click on Data Model node and select Import->DDIC structure to create entity type and entityset.

In the next screen enter below inputs to create Soheaderdata entitytype and SoheaderdataSet entityset

Select all fields in the next screen

Select Key field in the next screen which is SO_ID in this case.
Press Finish button to create entitytype and entityset.

Repeat above steps to create entity type and entity set for item and products using structures: BAPI_EPM_SO_ITEM and
BAPI_EPM_PRODUCT_HEADER.

Select SO_ID and SO_ITEM_POS as key fields for item entity type

Select PRODUCT_ID as key for product entity type


Now we have created 3 entity types and entity sets:

You can also see service implementations created for 3 entity sets.

Click on Generate run time objects button to create run time artifacts to implement CRUD operations

Classed generated:
Now expand service Maintenance folder and register ODATA service. And Then save project.

Alternatively you can also perform registering service manually using tcode: /IWFND/MAINT_SERVICE.

You will notice signal light turns to green in below screen shot

To Test ODATA Service, click on Maintain button on same screen:

Select ODATA row and click on call browser:


Now we will create associations and navigation property between Soheaderdata and Soitemdata and Soitemdata and ProductHeaderdata.

Specify association name, principal entity type name, dependent entity type name, Cardinality and Navigation Property in below window.

Specify referential constraints between Soheaderdata and Soitemdata and specify key relation in below window.
Finish setup in next window to create association and navigation property

Now create association and navigation property between Soitemdata and ProductHeaderdata similar way.
Delete below referential Constraints because there is no key relation
Now we are done with ODATA setup. In the next steps, we will implement data provider class methods to retrieve data.

In service implementation folder, for SoheaderdataSet entity set, we will implement method for GetEntitySet(Query) to retrieve all sales orders header
data.

To do this, go to Implementation class: ZCL_ZEPM_MODEL_ODATA_DPC_EXT in SE24 tcode and redefine method:


SOHEADERDATASET_GET_ENTITYSET.
This method will be called for below URL to retrieve sales orders header data:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet

We will also implement URI parameters Filter, Skip, Top and Orderby in the above URL.

To learn more about URI parameter in odata services, refer to below link:

http://www.odata.org/documentation/odata-version-2-0/uri-conventions/

Example URL’s with URI paramters:

For Filter URI on CurrencyCode field:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$filter=CurrencyCode eq ‘EUR’

For Skip and Top URI parameter: This will skip for 2 entries and display next top 10 sales orders.

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$skip=2&$top=10

For orderby URI parameter:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$orderby=GrossAmount

Copy below code in the method: SOHEADERDATASET_GET_ENTITYSET

Data declarations:

TYPES: tt_soorder TYPE STANDARD TABLE OF bapi_epm_so_header WITH EMPTY KEY.


TYPES: BEGIN OF ty_where,
cond(70),
END OF ty_where.
DATA: lt_where TYPE TABLE OF ty_where.
DATA: lt_sort_order TYPE STANDARD TABLE OF abap_sortorder.

Get Filter and orderby URI parameter values. Filter or selection URI parameters can retrieved using below method.

IF io_tech_request_context IS BOUND.
DATA(t_orderby) = io_tech_request_context->get_orderby( ).
ENDIF.
* Get filter or select option information
DATA(lo_filter) = io_tech_request_context->get_filter( ).
DATA(lt_filter_select_options) = lo_filter->get_filter_select_options( ).
DATA(lv_filter_str) = lo_filter->get_filter_string( ).

Build dynamic where condition and select sale orders header data

* Build dynamic where condition


DATA(lw_where) = CONV ty_where( lv_filter_str ).
APPEND lw_where TO lt_where.
SELECT *
FROM snwd_so
INTO TABLE @DATA(t_soheader)
WHERE (lt_where).

Handling page and skip:

* paging and skip


DATA(lw_end_row) = is_paging-top + 1.
IF is_paging-skip IS NOT INITIAL.
DELETE t_soheader FROM 1 TO is_paging-skip.
ENDIF.
IF is_paging-top IS NOT INITIAL..
DELETE t_soheader FROM lw_end_row.
ENDIF.

Perform orderby on result data set and populate et_entityset:

lt_sort_order = VALUE abap_sortorder_tab( FOR lw_orderby IN t_orderby LET lw_order = abap_true IN


( name = lw_orderby-property descending = lw_order ) ).
SORT t_soheader BY (lt_sort_order).
MOVE-CORRESPONDING t_soheader TO et_entityset.

To retrieve specific sales order data using below URL, we will implement method: SOHEADERDATASET_GET_ENTITY for GetEntity(Read).

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)

Redefine method in same class and copy below code in the method:

DATA: w_soheader TYPE bapi_epm_so_header.


DATA: w_so_id TYPE bapi_epm_so_id.
DATA(w_salord) = it_key_tab[ name = 'SoId' ]-value.
DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = in }| ).
w_so_id-so_id = w_vbeln.
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = w_so_id
IMPORTING
headerdata = w_soheader.
MOVE-CORRESPONDING w_soheader TO er_entity.

Now we will implement methods related to SoitemdataSet:

Method: SOITEMDATASET_GET_ENTITYSET to get all sales items for sales order using
URL: http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)/SalesOrderItem

Alternatively we can use URL: $expand=SalesOrderItem(Navigation property from


Soheaderdata) http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem
Redefine method SOITEMDATASET_GET_ENTITYSET in the class: ZCL_ZEPM_MODEL_ODATA_DPC_EXT

And Copy below code:

DATA: w_soheader TYPE bapi_epm_so_header.


DATA: w_so_id TYPE bapi_epm_so_id,
w_item_data TYPE STANDARD TABLE OF bapi_epm_so_item.
DATA(w_salord) = it_key_tab[ name = 'SoId' ]-value.
DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = IN }| ).
w_so_id-so_id = w_vbeln.
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = w_so_id
IMPORTING
headerdata = w_soheader
TABLES
itemdata = w_item_data.
et_entityset = CORRESPONDING zcl_zepm_model_odata_mpc=>tt_soitemdata( w_item_data ).

Below URL will calls methods SOHEADERDATASET_GET_ENTITYSET and SOITEMDATASET_GET_ENTITYSET in loop to retrieve all line items
related to all sales orders

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet?$expand=SalesOrderItem

Next we will implement SOITEMDATASET_GET_ENTITY to display sales item data for specific sales order and sales item. We can use below URL:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoitemdataSet(SoId=’500000000′,SoItemPos=’10’)

Alternatively we can use URL with navigation property:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000′)/SalesOrderItem(SoId=’500000000′,SoItemP
os=’10’)

Redefine method SOITEMDATASET_GET_ENTITY and copy below code:

DATA: w_soheader TYPE bapi_epm_so_header.


DATA: w_so_id TYPE bapi_epm_so_id,
t_so_item TYPE STANDARD TABLE OF bapi_epm_so_item.
* If navigation property used in URL, get key values from it_navigation_path importing parameter with path: SalesOrderItem
* Else use it_key_tab from importing parameter
IF it_navigation_path[] IS NOT INITIAL.
DATA(it_key_tab_tmp) = it_navigation_path[ nav_prop = 'SalesOrderItem' ]-key_tab.
ELSE.
it_key_tab_tmp = it_key_tab.
ENDIF.
DATA(w_salord) = it_key_tab_tmp[ name = 'SoId' ]-value.
DATA(w_vbeln) = CONV vbeln( |{ w_salord ALPHA = IN }| ).
w_so_id-so_id = w_vbeln.
DATA(w_salitem) = it_key_tab_tmp[ name = 'SoItemPos' ]-value.
DATA(w_posnr) = CONV vbeln( |{ w_salitem ALPHA = IN }| ).
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = w_so_id
IMPORTING
headerdata = w_soheader
TABLES
itemdata = t_so_item.
er_entity = CORRESPONDING zcl_zepm_model_odata_mpc=>ts_soitemdata( t_so_item[ so_item_pos = w_posnr ] ).

Below URL also retrieves all items related to single sales order “500000000”: This will call methods SOHEADERDATASET_GET_ENTITY and
SOITEMDATASET_GET_ENTITYSET .

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem

Next we will implement: PRODUCTHEADERDAT_GET_ENTITYSET method to retrieve product details:

Below URL retrieves product details related to sales order 500000000 and Item 10:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoitemdataSet(SoId=’500000000′,SoItemPos=’10’)/SalesOrderProduct
This URL calls Methods SOITEMDATASET_GET_ENTITY and then PRODUCTHEADERDAT_GET_ENTITYSET

Alternatively we can also use URL:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘5000000000′)/SalesOrderItem(SoId=’500000000′,SoItem
Pos=’10’)/SalesOrderProduct

This URL calls SOHEADERDATASET_GET_ENTITY and SOITEMDATASET_GET_ENTITY and then PRODUCTHEADERDAT_GET_ENTITYSET.

Below URL will retrieve all items and product info related to order: 5000000000

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/SoheaderdataSet(‘500000000’)?$expand=SalesOrderItem/SalesOrderProd
uct

This URL calls SOHEADERDATASET_GET_ENTITY and SOITEMDATASET_GET_ENTITYSET and then PRODUCTHEADERDAT_GET_ENTITYSET.

Below URL will retrieve all products info:

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/ProductHeaderdataSet

This URL is called PRODUCTHEADERDAT_GET_ENTITYSET.

Now redefine method: PRODUCTHEADERDAT_GET_ENTITYSET and copy below code:

Check for entries in it_key_tab and it_navigation_path to see if we need to retrieve product details related to a particular order/item or need to display all
product details. If both importing tables it_key_tab and it_navigation_path are empty then retrieve all products using BAPI_EPM_PRODUCT_GET_LIST
and populate records to et_entityset.

DATA: lw_product TYPE bapi_epm_product_id,


lw_header_product TYPE bapi_epm_product_header,
lw_header_so TYPE bapi_epm_so_header,
lw_orderno TYPE snwd_so_id,
lw_itemno TYPE snwd_so_item_pos,
lw_so_id TYPE bapi_epm_so_id.
DATA: lt_itemso TYPE STANDARD TABLE OF bapi_epm_so_item,
lt_products TYPE STANDARD TABLE OF bapi_epm_product_header.
IF it_key_tab[] IS INITIAL AND
it_navigation_path[] IS INITIAL.
CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_LIST'
TABLES
headerdata = lt_products.
MOVE-CORRESPONDING lt_products TO et_entityset.
ELSE.

In the else condition, check if URL has navigation property: SalesOrderItem, then get sales order and item number from it_navigation_path-key_tab OR
else use it_key_tab importing parameter.

* We can use below read(commented out) instead of conventional read statement, but if no navigation *property with
* SalesOrderItem is used in URL, below read will raise an exception: cx_sy_itab_line_not_found.
* With ABAP 7.5, we can use clause OPTIONAL along with below read so that it will not raise an
* exception and it would also set syst-subrc = 0. My current abap system is 7.4
* DATA(it_key_tab_tmp) = it_navigation_path[ nav_prop = 'SalesOrderItem' ]-key_tab.
READ TABLE it_navigation_path INTO DATA(w_navigation_path) WITH KEY nav_prop = 'SalesOrderItem'.
IF syst-subrc = 0.
DATA(it_key_tab_tmp) = w_navigation_path-key_tab.
ELSEIF it_key_tab IS NOT INITIAL.
it_key_tab_tmp = it_key_tab.
ENDIF.
DATA(lw_order_val) = it_key_tab_tmp[ name = 'SoId' ].
DATA(lw_item_val) = it_key_tab_tmp[ name = 'SoItemPos' ].
lw_orderno = |{ lw_order_val-value ALPHA = IN }|.
lw_itemno = |{ lw_item_val-value ALPHA = IN }|.
lw_so_id-so_id = lw_orderno.
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = lw_so_id
IMPORTING
headerdata = lw_header_so
TABLES
itemdata = lt_itemso.

lw_product = lt_itemso[ so_id = lw_orderno so_item_pos = lw_itemno ]-product_id.


CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
EXPORTING
product_id = lw_product
IMPORTING
headerdata = lw_header_product.
APPEND lw_header_product TO et_entityset.
ENDIF.

In the above code using sales order number and item number, getting corresponding product number. Use BAPI: BAPI_EPM_PRODUCT_GET_DETAIL
to retrieve product details for particular product and populate et_entityset.

Next we will redefine and implement method: PRODUCTHEADERDAT_GET_ENTITY to get product details related to particular product using below
URL

http://system:hostnumber/sap/opu/odata/sap/ZEPM_MODEL_ODATA_SRV/ProductHeaderdataSet(‘HT-1000’)

Copy below code:

DATA: lw_product TYPE bapi_epm_product_id,


lw_header_product TYPE bapi_epm_product_header.
lw_product-product_id = it_key_tab[ name = 'ProductId' ]-value.
CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'
EXPORTING
product_id = lw_product
IMPORTING
headerdata = lw_header_product.
MOVE-CORRESPONDING lw_header_product TO er_entity.

Association/navigation and data provider expand in OData


Associations define the relationship between two or more Entity Types (for example, Employee WorksFor
Department). Instances of associations are grouped in Association Sets.
Navigation Properties are special properties on Entity Types which are bound to a specific association and can be
used to refer to associations of an entity.
Finally, all instance containers (Entity Sets and Association Sets) are grouped in an Entity Container.

Reference – Overview | Open Data Protocol

Also let’s understand the difference between association/navigation and $expand. In short, it is as below,
Association/Navigatio Give me http://services.odata.org/OData/OData.svc/Categories(1)/Products?$format=json
n associated
(dependent)
entity/entities
using
Navigation
property

$expand Give me http://services.odata.org/OData/OData.svc/Categories(1)?$expand=Products&$format=jso


associated n
(dependent)
entity/entities
+ Principal
entity/entitie
s

using
Navigation
property

you can also refer this nice blog Implementing Expand Entity/Entity Set by Srikanth Gajula

Scenario
We will read Sales order, items and product data from Enterprise Procurement Model (EPM). This is the pictorial representation of sales order, items and
product with their association.

We will have 3 entities as displayed in below EDM diagram.


SalesOrder will be associated with SalesOrderItem and SalesOrderItem with Product.

Principle Entity Dependent Entity Navigation Property Cardinality

SalesOrder SalesOrderItem OrderToItems 1:N…

SalesOrderItem Product ItemToProduct 1:1

We will use below BAPIs to get the Sales Order, Items and Product data in DPC_EXT class methods.

●​ BAPI_EPM_SO_GET_LIST
●​ BAPI_EPM_SO_GET_DETAIL
●​ BAPI_EPM_PRODUCT_GET_DETAIL
We will code for association/navigation and data provider expand scenario and will also understand the framework expand.

Before that just look at difference between Data provider expand and framework expand

Framework Expand Data Provider Expand

Formerly called as generic expand Formerly called as basic expand

Requires no implementation effort Requires implementation effort

As this is handled by framework, same logic may In some cases, this provides better
be called multiple times in loop resulting in poor performance depending on how the code is
performance implemented

Procedure

Create entity SalesOrder by importing DDIC structure as shown below. Please note that Entity set will be created by
default if the check box “Create Default Entity Set” is checked.

Repeat the process for entities SalesOrderItem and Product. End result will be as displayed below.
Now let’s create association, navigation etc. By using Create Association wizard, it is just 3 steps as displayed below. This will create Navigation
property, Association set etc.

Create association between entities SalesOrder and SalesOrderItem with navigation property as OrderToItems.

On similar lines, create association between entities SalesOrderItem and Product with navigation property as ItemToProduct.
Please note that we do not have referential constraints created between SalesOrderItem and Product.

The final OData modeling will look like as below,

In this section, we will redefine methods in DPC_EXT class. Please note that code provided in this blog is in simplest form. You may need to consider
proper error handling and other best practices while writing the code.

Association/Navigation
First we will implement logic in method SALESORDERSET_GET_ENTITYSET by redefining it.

We will use below execution URI in Gateway Client (Transaction /IWFND/GW_CLIENT) to check the response payload.

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet

METHOD salesorderset_get_entityset.

DATA: lt_salesorder TYPE TABLE OF bapi_epm_so_header,

ls_salesorder LIKE LINE OF lt_salesorder,

ls_entity LIKE LINE OF et_entityset,

l_max_rows TYPE bapi_epm_max_rows.

l_max_rows-bapimaxrow = '10'.
CALL FUNCTION 'BAPI_EPM_SO_GET_LIST'

EXPORTING

max_rows = l_max_rows

TABLES

soheaderdata = lt_salesorder.

*Fill ET_ENTITYSET

LOOP AT lt_salesorder INTO ls_salesorder .

MOVE-CORRESPONDING ls_salesorder TO ls_entity.

APPEND ls_entity TO et_entityset.

ENDLOOP.

ENDMETHOD.

Redefine method SALESORDERSET_GET_ENTITY as below and then execute with below URI

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000009’)

METHOD salesorderset_get_entity.

DATA: ls_salesorder TYPE bapi_epm_so_header,

ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_soid TYPE bapi_epm_so_id.

*Get the key property values

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

lv_soid = ls_key_tab-value.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soid

IMPORTING

output = lv_soid.

CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'

EXPORTING

so_id = lv_soid

IMPORTING

headerdata = ls_salesorder.

*Fill ER_ENTITY

MOVE-CORRESPONDING ls_salesorder TO er_entity.

ENDMETHOD.

Redefine method SALESORDERITEMSE_GET_ENTITYSET as below with URI

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000008’)/OrderToItems

METHOD salesorderitemse_get_entityset.
DATA: ls_salesorder TYPE bapi_epm_so_header,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item,

ls_entity LIKE LINE OF et_entityset.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_soid TYPE bapi_epm_so_id.

*Get the key property values

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

lv_soid = ls_key_tab-value.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soid

IMPORTING

output = lv_soid.

CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'

EXPORTING

so_id = lv_soid

IMPORTING

headerdata = ls_salesorder

TABLES

itemdata = lt_itemdata.

LOOP AT lt_itemdata INTO ls_itemdata.

MOVE-CORRESPONDING ls_itemdata TO ls_entity .

APPEND ls_entity TO et_entityset.

ENDLOOP.

ENDMETHOD.

Notice that we used navigation property OrderToItems to get the associated entities.

Framework expand
By executing below URI, it will call framework expand by default.

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet?$expand=OrderToItems

This calls method SALESORDERSET_GET_ENTITYSET and SALESORDERITEMSE_GET_ENTITYSET in loop.

Check the values in header name sapgw-statistics for


URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet?$expand=OrderToItems&sap-statistics=true

This will give you performance statistics for OData request. For more information, refer Some new features in SAP NW Gateway 2.0 SP08
If we execute URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000009’)?$expand=OrderToItems

It will call method SALESORDERSET_GET_ENTITY and SALESORDERITEMSE_GET_ENTITYSET.

For below URI to work

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000000′)/OrderToItems(SoId=’0500000000′,SoItemPos=’0000000010’)

We need to implement logic with navigation property keys.

Alternatively we can read


as /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderItemSet(SoId=’0500000000′,SoItemPos=’0000000010′)

Let’s implement SALESORDERITEMSE_GET_ENTITY so that we can read data for above URI as

METHOD salesorderitemse_get_entity.

DATA: ls_salesorder TYPE bapi_epm_so_header.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_soid TYPE bapi_epm_so_id,

lv_soitempos TYPE snwd_so_item_pos,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item.

*Get the key property values

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

lv_soid = ls_key_tab-value.

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

lv_soitempos = ls_key_tab-value.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soid

IMPORTING

output = lv_soid.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soitempos
IMPORTING

output = lv_soitempos.

*Get data from BAPI

CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'

EXPORTING

so_id = lv_soid

IMPORTING

headerdata = ls_salesorder

TABLES

itemdata = lt_itemdata.

READ TABLE lt_itemdata INTO ls_itemdata WITH KEY so_id = lv_soid

so_item_pos = lv_soitempos.

IF sy-subrc = 0.

MOVE-CORRESPONDING ls_itemdata TO er_entity.

ENDIF.

ENDMETHOD.

Use of Navigation Path to read navigation keys


To read the data for below URI, we need to implement logic as below in method PRODUCTSET_GET_ENTITY

Execution URI –
/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000001′)/OrderToItems(SoId=’500000001′,SoItemPos=’0000000010’)/ItemToProduc
t

METHOD productset_get_entity.

DATA: ls_salesorder TYPE bapi_epm_so_header.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_soid TYPE bapi_epm_so_id,

lv_soitempos TYPE snwd_so_item_pos,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item.

DATA: ls_navigation TYPE /iwbep/s_mgw_navigation_path,

lv_property TYPE string.

DATA: lv_product_id TYPE bapi_epm_product_id,

lv_product_header TYPE bapi_epm_product_header.

IF iv_source_name = iv_entity_name.

*Get the key property values

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

IF sy-subrc = 0.

* MOVE-CORRESPONDING ls_itemdata to er_entity.

lv_product_id = ls_key_tab-value.
CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'

EXPORTING

product_id = lv_product_id

IMPORTING

headerdata = lv_product_header.

MOVE-CORRESPONDING lv_product_header TO er_entity.

ENDIF.

ELSE.

IF it_navigation_path IS NOT INITIAL.

READ TABLE it_navigation_path INTO ls_navigation INDEX 1.

IF sy-subrc EQ 0.

CASE ls_navigation-nav_prop.

WHEN 'OrderToItems'.

LOOP AT ls_navigation-key_tab INTO ls_key_tab.

CASE ls_key_tab-name.

WHEN 'SoId'.

lv_soid = ls_key_tab-value.

WHEN 'SoItemPos'.

lv_soitempos = ls_key_tab-value.

WHEN OTHERS.

ENDCASE.

ENDLOOP.

ENDCASE.

ENDIF.

ENDIF.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soid

IMPORTING

output = lv_soid.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soitempos

IMPORTING

output = lv_soitempos.

*Get data from BAPI_EPM_SO_GET_DETAIL

CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'

EXPORTING
so_id = lv_soid

IMPORTING

headerdata = ls_salesorder

TABLES

itemdata = lt_itemdata.

*Fill ER_ENTITY

READ TABLE lt_itemdata INTO ls_itemdata WITH KEY so_id = lv_soid

so_item_pos = lv_soitempos.

IF sy-subrc = 0.

lv_product_id-product_id = ls_itemdata-product_id.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'

EXPORTING

product_id = lv_product_id

IMPORTING

headerdata = lv_product_header.

MOVE-CORRESPONDING lv_product_header TO er_entity.

ENDIF.

ENDIF.

ENDMETHOD.

Also note that we can read product directly using URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/ProductSet(‘HT-1030’)

Now try with this URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000000′)?$expand=OrderToItems/ItemToProduct

Product details will not filled as in the navigation keys are empty because we do not have referential constraint.

Also try with /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderItemSet(SoId=’0500000000′,SoItemPos=’0000000020’)/ItemToProduct and


check why it is not working.

Additionally you can query as

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000000’)/$links/OrderToItems

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000009’)/$links/OrderToItems/$count

Data Provider Expand


In this section, we will see how to implement data provider expand by redefining GET_EXPANDED_ENTITYSET and GET_EXPANDED_ENTITY.

Implementing GET_EXPANDED_ENTITYSET

Let’s redefine GET_EXPANDED_ENTITYSET. With redefinition (just blank code) execute again the
URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet?$expand=OrderToItems

You will not get any response as you need to implement the code yourself.

One of the important point while implementing logic is Data declaration! Based on level till which you want to expand, you need to define your internal
table having nested structure or table type.

In below code, we want to expand Sales Order and its items. Hence the expand technical clause will be ORDERTOITEMS.

METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset.
DATA: BEGIN OF t_expand_so.

INCLUDE TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorder.

DATA: ordertoitems TYPE zcl_ztest_dp_expand_mpc_ext=>tt_salesorderitem,

END OF t_expand_so.

DATA: lt_expand_so LIKE TABLE OF t_expand_so,


D
ls_expand_so LIKE t_expand_so,

ls_item TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorderitem.

DATA: lt_salesorder TYPE TABLE OF bapi_epm_so_header,

ls_salesorder LIKE LINE OF lt_salesorder,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item,

l_max_rows TYPE bapi_epm_max_rows.

CONSTANTS: lc_expand_tech_clause TYPE string VALUE 'ORDERTOITEMS'.

* Read Sales Order and Item data

l_max_rows-bapimaxrow = '10'.

CALL FUNCTION 'BAPI_EPM_SO_GET_LIST'

EXPORTING

max_rows = l_max_rows

TABLES

soheaderdata = lt_salesorder

soitemdata = lt_itemdata.

* Data processing logic

LOOP AT lt_salesorder INTO ls_salesorder.

MOVE-CORRESPONDING ls_salesorder TO ls_expand_so .

LOOP AT lt_itemdata INTO ls_itemdata WHERE so_id = ls_salesorder-so_id .

MOVE-CORRESPONDING ls_itemdata TO ls_item .

APPEND ls_item TO ls_expand_so-ordertoitems.

CLEAR: ls_item.

ENDLOOP.data
APPEND ls_expand_so TO lt_expand_so.

CLEAR: ls_expand_so.

ENDLOOP.

* Fill EE_ENTITYSET

copy_data_to_ref(

EXPORTING

is_data = lt_expand_so

CHANGING

cr_data = er_entityset ).

* Insert Navigation property into ET_EXPANDED_TECH_CLAUSES


INSERT lc_expand_tech_clause INTO TABLE et_expanded_tech_clauses.

ENDMETHOD.

Query with URI /sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet?$expand=OrderToItems&sap-statistics=true to check the runtime


statistics and compare the values with before implementing data provider expand.

Now we will try to expand to one level more i.e. we will expand sales order, its items and product of each item. In this case the expand technical clause
will be ORDERTOITEMS/ITEMTOPRODUCT.

Below is the code we need to put to achieve the desired result.

METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset.

DATA: BEGIN OF t_orderitems.

INCLUDE TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorderitem.

DATA: itemtoproduct TYPE zcl_ztest_dp_expand_mpc_ext=>ts_product,

END OF t_orderitems.

DATA: BEGIN OF t_expand_so.

INCLUDE TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorder.

DATA: ordertoitems LIKE TABLE OF t_orderitems,

END OF t_expand_so.

DATA: lt_expand_so LIKE TABLE OF t_expand_so,

ls_expand_so LIKE t_expand_so,

ls_item LIKE t_orderitems.

DATA: lt_salesorder TYPE TABLE OF bapi_epm_so_header,

ls_salesorder LIKE LINE OF lt_salesorder,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item,

l_max_rows TYPE bapi_epm_max_rows.

DATA: lv_product_id TYPE bapi_epm_product_id,

ls_product_header TYPE bapi_epm_product_header.

CONSTANTS: lc_expand_tech_clause TYPE string VALUE 'ORDERTOITEMS/ITEMTOPRODUCT'.


* Read Sales Order and Item data

l_max_rows-bapimaxrow = '10'.

CALL FUNCTION 'BAPI_EPM_SO_GET_LIST'

EXPORTING

max_rows = l_max_rows

TABLES

soheaderdata = lt_salesorder

soitemdata = lt_itemdata.

* Data processing logic

LOOP AT lt_salesorder INTO ls_salesorder.

MOVE-CORRESPONDING ls_salesorder TO ls_expand_so .

LOOP AT lt_itemdata INTO ls_itemdata WHERE so_id = ls_salesorder-so_id .

MOVE-CORRESPONDING ls_itemdata TO ls_item .

lv_product_id = ls_itemdata-product_id.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'

EXPORTING

product_id = lv_product_id

IMPORTING

headerdata = ls_product_header.

MOVE-CORRESPONDING ls_product_header TO ls_item-itemtoproduct.

APPEND ls_item TO ls_expand_so-ordertoitems.

CLEAR: ls_item.

ENDLOOP.

APPEND ls_expand_so TO lt_expand_so.

CLEAR: ls_expand_so, lv_product_id.

ENDLOOP.

* Fill EE_ENTITYSET

copy_data_to_ref(

EXPORTING

is_data = lt_expand_so

CHANGING

cr_data = er_entityset ).

* Insert Navigation property into ET_EXPANDED_TECH_CLAUSES

INSERT lc_expand_tech_clause INTO TABLE et_expanded_tech_clauses.

ENDMETHOD.

Please note that we need to insert complete expand clause in et_expanded_tech_clauses

Implementing GET_EXPANDED_ENTITY
Let’s implement GET_EXPANDED_ENTITY.
Below is the query to execute and code to for implementation.

/sap/opu/odata/sap/ZTEST_DP_EXPAND_SRV/SalesOrderSet(‘500000005’)?$expand=OrderToItems/ItemToProduct

METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entity.

DATA: BEGIN OF t_orderitems.

INCLUDE TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorderitem.

DATA: itemtoproduct TYPE zcl_ztest_dp_expand_mpc_ext=>ts_product,

END OF t_orderitems.

DATA: BEGIN OF t_expand_so.

INCLUDE TYPE zcl_ztest_dp_expand_mpc_ext=>ts_salesorder.

DATA: ordertoitems LIKE TABLE OF t_orderitems,

END OF t_expand_so.

DATA: ls_expand_so LIKE t_expand_so,

ls_item LIKE t_orderitems.

DATA: ls_salesorder TYPE bapi_epm_so_header,

lt_itemdata TYPE TABLE OF bapi_epm_so_item,

ls_itemdata TYPE bapi_epm_so_item.

DATA: ls_key_tab TYPE /iwbep/s_mgw_name_value_pair,

lv_soid TYPE bapi_epm_so_id.

DATA: lv_product_id TYPE bapi_epm_product_id,

ls_product_header TYPE bapi_epm_product_header.

CONSTANTS: lc_expand_tech_clause TYPE string VALUE 'ORDERTOITEMS/ITEMTOPRODUCT'.

*Get the key property values and Read Sales Order and Item data

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

lv_soid = ls_key_tab-value.

CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'

EXPORTING

input = lv_soid

IMPORTING

output = lv_soid.

CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'

EXPORTING

so_id = lv_soid

IMPORTING

headerdata = ls_salesorder

TABLES

itemdata = lt_itemdata.
* Data processing logic

MOVE-CORRESPONDING ls_salesorder TO ls_expand_so .

LOOP AT lt_itemdata INTO ls_itemdata WHERE so_id = ls_salesorder-so_id .

MOVE-CORRESPONDING ls_itemdata TO ls_item .

lv_product_id = ls_itemdata-product_id.

CALL FUNCTION 'BAPI_EPM_PRODUCT_GET_DETAIL'

EXPORTING

product_id = lv_product_id

IMPORTING

headerdata = ls_product_header.

MOVE-CORRESPONDING ls_product_header TO ls_item-itemtoproduct.

APPEND ls_item TO ls_expand_so-ordertoitems.

ENDLOOP.

* Fill ER_ENTITY

copy_data_to_ref(

EXPORTING

is_data = ls_expand_so

CHANGING

cr_data = er_entity ).

* Insert Navigation property into ET_EXPANDED_TECH_CLAUSES

INSERT lc_expand_tech_clause INTO TABLE et_expanded_tech_clauses.

ENDMETHOD.

Important points to be considered while coding for association/navigation and data provider $expand,

●​ Use of navigation path and navigation key. Entities can be directly accessed or via navigation property. Code for both scenario
using navigation path and navigation keys.
●​ Data declaration of internal tables in case of data provider expand. Understand the relations between entities. while declaring
internal table, use navigation property name to address dependent entity structure. It should be same. Check below data
declaration.

DATA: BEGIN OF t_orderitems.

INCLUDE TYPE zcl_ztest_fw_expand_mpc_ext=>ts_salesorderitem.

DATA: itemtoproduct TYPE zcl_ztest_fw_expand_mpc_ext=>ts_product,

END OF t_orderitems.

DATA: BEGIN OF t_expand.

INCLUDE TYPE zcl_ztest_fw_expand_mpc_ext=>ts_salesorder.

DATA: ordertoitems LIKE TABLE OF t_orderitems,

END OF t_expand.

DATA: lt_so LIKE TABLE OF t_expand,

ls_so LIKE t_expand,

ls_item LIKE t_orderitems.


●​ Parent and its immediate child will be separated using “/” for e.g $expand=OrderToItems/ItemToProduct if we would have 2nd
sibling at order level for e.g Partners of Sales order then we could have navigation property as OrderToPartners with say its
child as PartnerToAddress then we need to access it as OrderToPartners/PartnerToAddress. To get the expanded result for
both hierarchies, the expand clause will look as $expand=OrderToItems/ItemToProduct,OrderToPartners/PartnerToAddress
(separated by “,”)
●​ Navigation property separated with “/” will be inserted into expanded technical clause. 2nd hierarchy will be appended in
expanded technical clause
In short, for example mentioned in point 3, it would be,

ls_expanded_clause_items = 'ORDERTOITEMS/ITEMTOPRODUCT'.

ls_expanded_clause_partners = 'ORDERTOPARTNERS/PARTNERTOADDRESS'.

APPEND ls_expanded_clause_items TO et_expanded_tech_clauses.

APPEND ls_expanded_clause_partners TO et_expanded_tech_clauses.

Function import in odata service


●​ Function Imports are the actions like the release of a sales order/purchase order executed in the back-end
system. They can be used whenever the given requirement does not fit into the CRUD-Q operations.
●​ If the business scenario not able to manage through the default CRUD operation at that time go to the function
import option

●​ We have a table in which it contains customer information, two or more customers' information have to be
approved at once. In this scenario to approve each customer information we will use Function Import.
●​ In OData every operation needs a separate method to trigger for function import we have method called
‘Execute_action’
Example-2

●​ What ever coming from UI function import we have to create a properties


●​ Return Type Kind:If you want the function import to return data you can set this field. We have 3 possible options
available
●​ Complex Type – if the return data is of complex entity type, select this option
●​ Entity Type – if the return data is of entity type, select this option and
●​ No return – if you do not want to return anything, select this option.
●​ Return Type:This field depends on the option you have choose above. You need to specify the name of the type in
this field. Use the input help to choose from existing types or create a new type.
●​ Some technical information
Function import need a medium to return the value i.e entity type & entity set
Map function import with entity and entity set
URI- /sap/opu/odata/sap/ZDEBIDATTA_FUNCTION_IMPORT_SRV/FI_Ccode?Ccode='0001'&MAX=10

LINK-https://saplearners.com/function-import-in-sap-odata-service/

Read/write data via a deep entity in OData

1.​ Tables involved: VBAK(Sales Document: Header Data), VBAP(Sales Document: Item Data).
2.​ Create a Project in SEGW(SAP Gateway Service Builder).
3.​ Import the DDIC Structure: VBAK(Sales Document: Header Data).

Import the DDIC 4.


Structure: VBAP(Sales Document: Item Data)
4.​ Create Association​

5.​ Go to Runtime Artifacts node, open the ZCL_ZGW_PRACTICE006_MPC_EXT class in ABAP


Workbench(Right-Click: Go to ABAP Workbench) & click on the Types tab.

6.​ Click on the Change(Ctrl+F1) button for editing.


7.​ Click on the Direct Type Entry button. Then, create the deep structure & activate.

8.​ *--------------------------------------------------------------------*
9.​ * Deep Structure
10.​*--------------------------------------------------------------------*
11.​ TYPES
12.​: BEGIN OF ty_deep_entity,
13.​ vbeln TYPE vbeln_va, "Sales Document
14.​ erdat TYPE erdat, "Date on which the record was created
15.​ erzet TYPE erzet, "Entry time
16.​ ernam TYPE ernam, "Name of Person who Created the Object
17.​ vkorg TYPE vkorg, "Sales Organization
18.​* Navigation property name should be used otherwise empty records will be shown
19.​ headertoitem TYPE TABLE OF ts_sales_item_data WITH DEFAULT KEY,
20.​ END OF ty_deep_entity.

*--------------------------------------------------------------------*​

N.B:

The navigation property name should be used in case of a deep entity like shown in the image above
otherwise, empty records will be returned.

Do not regenerate the service before taking the backup as it will delete all the custom structures.

Just redefine the basic methods: *GET_ENTITY & *GET_ENTITYSET of the entities for easy
troubleshooting. No need to write any code within the methods. The $expand keyword will call only
the GET_EXPANDED_ENTITYSET method.
21.​Go to the ZCL_ZGW_PRACTICE006_DPC_EXT class & redefine the
method: GET_EXPANDED_ENTITYSET.

22.​ METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entityset.


23.​
24.​ DATA : lt_deep_entity TYPE TABLE OF zcl_zgw_practice006_mpc_ext=>ty_deep_entity,
"Deep Entity Type
25.​ ls_deep_entity TYPE zcl_zgw_practice006_mpc_ext=>ty_deep_entity,
"Deep Entity Type
26.​ ls_item TYPE zcl_zgw_practice006_mpc_ext=>ts_sales_item_data.
27.​
28.​* Based on the entity set
29.​ IF iv_entity_set_name = 'Sales_Header_DataSet'.
30.​ SELECT * FROM vbak
31.​ INTO TABLE @DATA(lt_vbak)
32.​* UP TO 20 ROWS.
33.​ WHERE vbeln = '0000018338'. "According to the requirements
34.​ IF sy-subrc = 0.
35.​ SELECT * FROM vbap
36.​ INTO TABLE @DATA(lt_vbap)
37.​ FOR ALL ENTRIES IN @lt_vbak
38.​ WHERE vbeln = @lt_vbak-vbeln.
39.​ ENDIF.
40.​ IF lt_vbak IS NOT INITIAL AND lt_vbak IS NOT INITIAL.
41.​ SORT lt_vbak DESCENDING BY vbeln.
42.​ SORT lt_vbap DESCENDING BY vbeln.
43.​ LOOP AT lt_vbak INTO DATA(ls_vbak_item).
44.​ ls_deep_entity = CORRESPONDING #( ls_vbak_item ).
45.​ IF line_exists( lt_vbap[ vbeln = ls_deep_entity-vbeln ] ).
46.​ LOOP AT lt_vbap INTO DATA(ls_vbap_item) FROM sy-tabix.
47.​ IF ls_vbak_item-vbeln <> ls_vbap_item-vbeln.
48.​ EXIT.
49.​ ENDIF.
50.​ ls_item = CORRESPONDING #( ls_vbap_item ).
51.​* Appending to the deep structure
52.​ APPEND ls_item TO ls_deep_entity-headertoitem.
53.​ CLEAR ls_item.
54.​ ENDLOOP.
55.​ APPEND ls_deep_entity TO lt_deep_entity.
56.​ ENDIF.
57.​ CLEAR ls_vbak_item.
58.​ ENDLOOP.
59.​ ENDIF.
60.​
61.​* For converting the data in the deep structure to the output format
62.​ CALL METHOD me->/iwbep/if_mgw_conv_srv_runtime~copy_data_to_ref
63.​ EXPORTING
64.​ is_data = lt_deep_entity
65.​ CHANGING
66.​ cr_data = er_entityset.
67.​
68.​ ENDIF.
69.​

ENDMETHOD.​

70.​Go to the TCODE: /N/IWFND/MAINT_SERVICE & REGISTER the service by providing the service name &
the Alias name(Central Hub Only).
71.​Go to the TCODE: /N/IWFND/GW_CLIENT for testing the service. Enter the below URI in the Request
URI section.

/sap/opu/odata/sap/ZGW_PRACTICE006_SRV/Sales_Header_DataSet?$expand=HeaderToItem&$forma
t=json​
72.​Output:
Page | 95

You might also like