ABAP Blog Posts
cancel
Showing results for 
Search instead for 
Did you mean: 
shasankgupta024
Participant
341

Scenario:
Suppose we need to expose images related to a "PickTicket" from an external system, making them available in SAP via a custom OData service.

Steps Overview

1. Create a Custom Entity CDS View
2. Create Service Definition
3. Create Service Binding
4. Implement ABAP Class to Consume the External API
5. Test the OData Service

1. Create a Custom Entity (CDS View)

Start by defining a **custom entity** to represent the data structure for the API response:

@EndUserText.label: 'Custom Entity for Image Data'
@ObjectModel: {
query: {
implementedBy: 'ABAP:ZCL_IMAGESET'
}
}
define root custom entity ZCE_IMAGESET
{
key pickticketid : abap.char(20);
key urlshortlivedthumbnail : abap.char(255);
trigger_ : abap.char(20);
source : abap.char(40);
imageid : abap.char(20);
urlthumbnail : abap.char(255);
urlshortlived : abap.char(255);
}

This entity acts as a **virtual view**—data retrieval is handled by an ABAP class (see `implementedBy` above).

2. Create a Service Definition

Expose the custom entity in your service definition:

@EndUserText.label: 'zsd_imageset'
define service Zsd_imageset {
expose ZCE_IMAGESET;
}

3. Create a Service Binding

Next, create a **service binding**—for example, `ZSB_IMAGESET` for OData V4—which generates the OData endpoint for your application. *Use the standard RAP tools in Eclipse or ADT to create this binding from the CDS service definition.*

4. Implement the ABAP Class to Handle API Call

Define the ABAP class (`zcl_imageset`) to fetch and provide data from the external API.
Use the **RAP Query Provider Interface** (`if_rap_query_provider`) for custom data provisioning:

Class Definition

CLASS zcl_imageset DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .

PUBLIC SECTION.

TYPES: BEGIN OF ty_imageset,
trigger TYPE char20,
pickticketid TYPE char20,
source TYPE char40,
imageid TYPE char20,
urlthumbnail TYPE char255,
urlshortlived TYPE char255,
urlshortlivedthumbnail TYPE char255,
END OF ty_imageset.

TYPES: tt_imageset TYPE STANDARD TABLE OF ty_imageset WITH EMPTY KEY.

INTERFACES if_rap_query_provider.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.

Class Implementation

CLASS ZCL_IMAGESET IMPLEMENTATION.

METHOD if_rap_query_provider~select.

DATA lt_images TYPE tt_imageset.
DATA lv_pickticketid TYPE string.

" Get filter (PickTicketId)

DATA(lo_filter) = io_request->get_filter( )->get_as_sql_string( ).
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
DATA(lv_max_rows) = COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited THEN 0
ELSE lv_page_size ).
DATA(lt_sort_table) = VALUE string_table( FOR ls_sort_element IN io_request->get_sort_elements( )
( ls_sort_element-element_name && COND #( WHEN ls_sort_element-descending = abap_true THEN ` descending` ELSE ` ascending` ) ) ).
CONCATENATE 'PICKTICKETID ' 'SOURCE'
INTO DATA(lv_default_sort) SEPARATED BY ',' .
DATA(lv_sort_string) = COND #( WHEN lt_sort_table IS INITIAL THEN lv_default_sort
ELSE concat_lines_of( table = lt_sort_table sep = ',' ) ).


IF io_request IS BOUND.
DATA(lt_expressions) = io_request->get_filter( )->get_as_ranges( ).


LOOP AT lt_expressions INTO DATA(ls_expr).
IF ls_expr-name = 'PICKTICKETID'.
LOOP AT ls_expr-range INTO DATA(ls_range).
IF ls_range-option = 'EQ'.
lv_pickticketid = ls_range-low.
EXIT.
ENDIF.
ENDLOOP.
EXIT.
ENDIF.
ENDLOOP.

ENDIF.

" Build target URL for the external request
DATA(lv_url) = |https://<external domain url>/pickTickets/{ lv_pickticketid }/images|.

" Create HTTP client and set headers
DATA lo_http_client TYPE REF TO if_http_client.
cl_http_client=>create_by_url(
EXPORTING url = lv_url
IMPORTING client = lo_http_client
).
lo_http_client->request->set_header_field( name = 'client_id' value = '<your_client_id>' ).
lo_http_client->request->set_header_field( name = 'client_secret' value = '<your_client_secret>' ).
lo_http_client->send( ).
lo_http_client->receive( ).
DATA(lv_response) = lo_http_client->response->get_cdata( ).
lo_http_client->close( ).

" Parse JSON response to ABAP table
DATA(lo_json) = NEW /ui2/cl_json( ).
lo_json->deserialize(
EXPORTING json = lv_response
CHANGING data = lt_images
).

" Return the data
io_response->set_total_number_of_records( lines( lt_images ) ).
io_response->set_data( lt_images ).

ENDMETHOD.

ENDCLASS.

*Replace* `<external domain url>`, `<your_client_id>`, and `<your_client_secret>` *with real values.*

5. Publish and Test Your OData Service

  • Use `/IWFND/MAINT_SERVICE` or the service binding UI in Eclipse/ADT to publish your OData service.
  • Test the endpoint with a filter on `PickTicketId`:
    Example: /sap/opu/odata/sap/ZSB_IMAGESET/ZCE_IMAGESET?$filter= pickticketid eq '123456'

Key Takeaways

  • Custom Entities in RAP allow you to virtualize data from external sources as standard OData.
  • You can leverage the **HTTP client functionalities** in ABAP to fetch data from any REST API.
  • RAP and ABAP Cloud stack are open for API-driven architectures.
  • Make sure to treat secrets and API credentials securely—avoid hardcoding in productive code.

Conclusion

You now have a **custom OData service** in SAP S/4HANA PCE, powered by the RAP framework, which brings in dynamic data from an **external API**. You can enhance, secure, and extend this pattern for many integration use cases—be it analytics dashboards, mobile apps, or UI5/Fiori apps!

Let me know your feedback, or reach out for specific questions.