Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
florian_moritz
Advisor
Advisor
INFO: This blog is part of a tutorial series for the standalone SAP Graph service on SAP BTP. Recently, SAP released a version of Graph in SAP Integration Suite with better user experience and functional enhancements (see this blog for details: https://blogs.sap.com/2023/02/22/announcing-graph-in-sap-integration-suite).




 

 

What you will learn: use SAP Graph with OData to fetch data and explore the structure of the unified data model exposed by SAP Graph.

Hello!

In this fourth part of the tutorial on SAP Graph - the unified API for SAP’s Integrated Intelligent Suite - we will focus on the data protocol that SAP Graph uses, OData, to explore the structure of the unified data model exposed by SAP Graph. Please refer to the information map to see all parts of this tutorial series.

OData - the protocol used by SAP Graph


SAP Graph is built on open standards and technologies. Clients communicate with SAP Graph using the Open Data Protocol (OData). This is a standardized RESTful HTTP protocol with defined semantics that help promote interoperability between services. SAP Graph follows the OData V4 protocol.

OData is centered around resources (data entities), which are identified by URLs. In SAP Graph, there are for example the Customer, Product or SalesQuote entities. The structure of these entities is defined in an Entity Data Model (EDM) which can be inspected by clients. OData provides many operations to filter or search in entity collections, to navigate between associated entities and to adapt the response format.

With OData, SAP Graph exposes a single unified API with defined semantics and a unified data model, for all SAP-managed data.

We will now look at examples on how to formulate OData requests for SAP Graph.

Tutorial setup


To keep things simple, we will be using the SAP Graph sandbox endpoint. All you need to use this endpoint is your favorite HTTP tool, for example Postman, and your API key from SAP API Hub. To retrieve your API key, log into SAP API Business Hub, go to settings and copy your API key from the Show API Key button.

To make requests against the SAP Graph sandbox, add your API key as an HTTP header in your requests: apiKey: <your API key>. Then you can use the SAP Graph sandbox through the following endpoint:

https://sandbox.api.sap.com/sapgraph/

 

Alternatively, you could make requests against the SAP Graph sandbox in a web browser using the lightweight app you configured in part 2 of this tutorial series.


Note: The API key is just used for the purposes of this sandbox endpoint and not relevant in the context of OData requests or productive SAP Graph instances.

Also note, that the URL of the SAP Graph sandbox differs from the productive URL of SAP Graph: https://eu30.graph.sap/api.

Exploring the OData data model


OData defines an Entity Data Model that describes the structure of all known entities. This helps developers and clients alike to reason about the available entities and their structure. The data model and further metadata can be inspected through a special $metadata endpoint. SAP Graph makes metadata available for each namespace, such as sap.graph for the unified SAP Graph data model.

To retrieve the metadata of the unified data model in SAP Graph, make the following request, which will return an EDMX specification (an XML dialect for describing OData Entity Data Models😞

In the response, we can inspect the structure of all the entities (and sub-entities) that are described as EntityType entries as well as other metadata. Let's have a closer look at the definition of the SalesQuote entity type in this extract from the sales domain metadata:
...
<EntityType Name="SalesQuote">
<Key>
<PropertyRef Name="id"/>
</Key>
<Property Name="id" Type="Edm.String" MaxLength="82" Nullable="false"/>
<Property Name="displayId" Type="Edm.String"/>
<Property Name="netAmount" Type="Edm.Decimal" Scale="6" Precision="22"/>
<Property Name="pricingDate" Type="Edm.DateTimeOffset"/>
<Property Name="netAmountCurrency" Type="Edm.String" MaxLength="5"/>
<NavigationProperty Name="_netAmountCurrency" Type="sap.graph.Currency"/>
<NavigationProperty Name="items" Type="Collection(sap.graph.SalesQuote_items)" Partner="up_" ContainsTarget="true"/>
<Property Name="soldToParty" Type="Edm.String" MaxLength="10"/>
<NavigationProperty Name="_soldToParty" Type="sap.graph.Customer"/>
<NavigationProperty Name="_cxsales" Type="sap.cxsales.SalesQuoteCollection"/>
...
</EntityType>
...

We can see that a SalesQuote has several properties of different types, such as String or Decimal, but also more complex structured types like the items property, which is a collection of several sap.graph.SalesQuote_items, that are also defined in the same data model. Furthermore, we see that the id property is referenced as key for this entity type. Navigation Properties allow for navigation to a related entity. The items property is a navigation property because the sap.graph.SalesQuote_items is modeled as a separate entity type in the metadata.

Let's look at an example. Add the following query path to the sandbox endpoint to make the request. This will retrieve one SalesQuote entity (here we add $top=1 to limit the result set to one entity):

/sap.graph/SalesQuote?$top=1


From inspecting the metadata, we have learned that SalesQuotes have items, however when comparing with the response from the example, no items are included in the response.

The reason for this is that OData has a mechanism for the client to control which navigation properties are included as part of the response. This is called expanding a property. By default, SAP Graph returns responses non-expanded if not specified by the client. In addition to expanding the response, OData also allows for restricting it to requested values only. We will have a look at these mechanisms next.

Expanding and restricting the response


OData allows clients to adapt the response format in two ways. Clients can restrict the response to a set of specified properties via $select. And they can expand the response by including referenced entities inline as part of the response via $expand.

If we only require the netAmount of a SalesQuote, we can restrict the response by adding it to the $select query parameter:

/sap.graph/SalesQuote?$top=1&$select=netAmount


This will return only the netAmount property along with the id, as it is the key and therefore always included.

If we also require the items of a SalesQuote, we need to expand the response (as it is a navigation property) by adding it to the $expand query parameter:

/sap.graph/SalesQuote?$top=1&$select=netAmount&$expand=items


This will result in the items array being returned as part of the SalesQuote response as illustrated here:


If we have a look at a response snippet, this is what is being returned:
{
"@odata.context": "$metadata#SalesQuote(netAmount,id,items())",
"value": [
{
"id": "cxsales~1",
"netAmount": 890,
"items": [
{
"itemId": "10",
"parentItemId": "",
"alternativeToItemId": "",
"itemCategory": "AGN",
"itemText": "Green Emission Calculator",
"product": "P300100",
"soldToPartyProductId": "",
"quantity": 1,
"quantityUnit": "EA",
"grossWeight": 0,
"grossWeightUnit": "",
"netWeight": 0,
"netWeightUnit": "",
"volume": 0,
"volumeUnit": "",
"plant": "",
"netAmount": 890,
"netAmountCurrency": "USD",
"pricingProduct": "",
"incotermsClassification": "",
"incotermsLocation": "",
"processingStatus": "1",
"cancellationReason": ""
}
]
}
]
}

Note that we do not have to specify expanded properties explicitly as part of $select.

We see that the item in the example above has a property product that references an id. When we inspect the metadata of the items by searching for the EntityType SalesQuote_items. We see that the product property represents the referenced product with a string value and the _product navigation property references the connected sap.graph.Product entity.
...
<EntityType Name="SalesQuote_items">
<Key>
<PropertyRef Name="itemId"/>
</Key>
<Property Name="itemId" Type="Edm.String" MaxLength="10" Nullable="false"/>
<Property Name="itemText" Type="Edm.String"/>
<Property Name="product" Type="Edm.String"/>
<NavigationProperty Name="_product" Type="sap.graph.Product"/>
...
</EntityType>
...

To also include this _product as part of the response, we can expand it inside the already expanded items: a nested expand.

/sap.graph/SalesQuote?$top=1&$select=netAmount&$expand=items($expand=_product)


Which will include the _product response inline in the item of the SalesQuote response.


If we also want to adapt the format of the expanded product we can nest a $select query parameter in parentheses after the product. To restrict the nested product to the displayId, for example:

/sap.graph/SalesQuote?$top=1&$select=netAmount&$expand=items($expand=_product($select=displayId))


If we also want to select more properties, such as netAmountCurrency, we can just add them to the $select query parameter targeting the SalesQuote, separating by commas:
/sap.graph/SalesQuote?$top=1&$select=netAmount,netAmountCurrency&$expand=items($expand=_product($select=displayId))

The response for the SalesQuote with included netAmountCurrency, items and _product now looks like this:
{
"@odata.context": "$metadata#SalesQuote(netAmount,netAmountCurrency,id,items(_product(displayId,id)))",
"value": [
{
"id": "cxsales~1",
"netAmount": 890,
"netAmountCurrency": "USD",
"items": [
{
"itemId": "10",
"parentItemId": "",
"alternativeToItemId": "",
"itemCategory": "AGN",
"itemText": "Green Emission Calculator",
"product": "P300100",
"_product": {
"id": "cxsales~P300100",
"displayId": null
},
"soldToPartyProductId": "",
"quantity": 1,
"quantityUnit": "EA",
"grossWeight": 0,
"grossWeightUnit": "",
"netWeight": 0,
"netWeightUnit": "",
"volume": 0,
"volumeUnit": "",
"plant": "",
"netAmount": 890,
"netAmountCurrency": "USD",
"pricingProduct": "",
"incotermsClassification": "",
"incotermsLocation": "",
"processingStatus": "1",
"cancellationReason": ""
}
]
}
]
}


Working with collections: filtering, ordering and pagination


When working with collections of entities, we typically want to filter the entities by some criteria or arrange them in a specific order. In OData this is supported via the $filter and $order query parameters. With $top and $skip we can additionally define a sliding window over the result to implement pagination.

To continue with our example above, let's remove the $top=1 we added initially to retrieve a collection of SalesQuotes. Say we want to retrieve all SalesQuotes with a value greater than 100 U.S. Dollars.

The value condition can be formulated with the greater equal operator: netAmount ge 100. For the currency condition we need to compare it with a string (enclosed in single quotes) for equality: netAmountCurrency eq 'USD'. We then combine these conditions with a logical and expression and add it to the example query below. We also define an ordering on the netAmount property with the $orderby query parameter, which is ascending by default.
Note: OData operators are lower-case and space-separated. This requires URL encoding. If you are using Postman for this tutorial you don't need to worry as Postman automatically applies URL encoding.

/sap.graph/SalesQuote?$select=netAmount,netAmountCurrency&$expand=items($expand=_product($select=displayId))&$filter=netAmount ge 100 and netAmountCurrency eq 'USD'&$orderby=netAmount

Note: OData only supports writing filter expressions using a small vocabulary of filter operators. See here for a full list.

As a last step, we will now define a paging-window over all results. This can be achieved with the query parameters $top, which specifies how many result entities should be returned: the page size, and $skip, which defines how many result entities should be skipped from the beginning of the ordering. Hence, we describe the page size with $top and index the single pages with multiples of the page size in $skip. For the second page of five entities each, this would translate to our example as follows:
/sap.graph/SalesQuote?$select=netAmount,netAmountCurrency&$expand=items($expand=_product($select=displayId))&$filter=netAmount ge 100 and netAmountCurrency eq 'USD'&$orderby=netAmount&$top=5&$skip=5


Summary


In this tutorial we had a look at OData, the data protocol used by SAP Graph. We covered all the features that you as a developer working with SAP Graph need to know, like how to:

  • formulate complex queries

  • inspect the metadata and structure of the unified data model

  • expand and restrict responses

  • work with collections


OData itself offers much more than what we showed in this tutorial. You can read more about it in the standard.

With SAP Graph, you as a developer now have one data protocol you can use together with one API to retrieve data in one unified format, no matter the source system.




Florian Moritz, User Experience Developer – SAP Graph

Visit the SAP Graph website at https://navigator.graph.sap

Contact us at sap.graph@sap.com




 
3 Comments