Afin d’appeler et de traiter facilement des APIs en ABAP, il existe un repository object très utile et facile fourni par SAP : le Service Consumption Model 2.
Ce Service Consumption Model permet la création automatique d’un proxy qu’il est ensuite possible d’appeler via une classe. Ce proxy permet de facilement gérer les structures de retours de l’API, de gérer les paramètres d’appels (GET, POST, DELETE, PUT, PACTH), les différents services, etc…
Nous allons voir en détails comment générer ce proxy et comment s’en servir. Nous verrons également en quoi il est plus facile de l’utiliser plutôt que de gérer la gestion du traitement de l’API manuellement dans le code ABAP.
Pour cet exemple, nous utiliserons l’API Northwind.
Creation du Proxy via Service Consumption Model
1/ Aller dans New > Other Repository Object >Service Consumption Model
2/ Donner un nom au Service Consumption Model
3/ Donner un nom au proxy généré et forunir le fichier EDMX contenant les Metadatas de l’API : https://services.odata.org/v4/northwind/northwind.svc/$metadata
4/ Cela créera toutes les objets nécessaires au traitements pour appeler l’API et ces services
5/ Il est possible d’ajouter un Etag, ce qui n’est pas fait ici
6/ Enfin, le Service Consumption model est créé et propose désormais des templates pour appeler chaque endpoints disponible dans l’API avec chaque méthode disponible (GET, POST, …).
On voit par ailleurs que ces templates viennent appeler le proxy généré précédemment.
Note : Cette fonctionnalité n’étant présente que sur BTP, ABAP Environment et S/4 Hana Public Cloud, il existe une manière contournée d’utiliser le Service Consumption Model pour S/4 Hana On-Premise et Private Cloud en utilisant le BTP ABAP Environment, Trial Edition.
Exemple d’appel
Dans notre cas, nous décidons d’appeler le service « Products »
Nous copions-collons donc le template proposé avec 2 changements.
- Comme il n’y a pas d’authentification demandé pour cette API, nous créons directement la destination http via cl_http_destination_provider=>create_by_url
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( 'https://services.odata.org' ).
lo_http_client = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
Dans le cas où un système d’authentification était mis en place, je conseille de passer par un communication arrangement, dont voici la documentation :
Note : On passera plutôt par une destination en SM59 sur des systèmes On-Premise ou Private Cloud.
- Dans la création du proxy, nous précisons le chemin pour appeler l’API (attention, ne pas préciser l’entity, ici « /Products » qui va être appelée puisque gérée plus tard lors de l’appel API avec le proxy )
lo_client_proxy = /iwbep/cl_cp_factory_remote=>create_v4_remote_proxy(
EXPORTING
is_proxy_model_key = VALUE #( repository_id = 'DEFAULT'
proxy_model_id = 'ZTEST_NORTHWIND'
proxy_model_version = '0001' )
io_http_client = lo_http_client
iv_relative_service_root = '/v4/northwind/northwind.svc' ).
Explication du code
1/ Créer la destination HTTP avec l’URL du système cible et créer le proxy pour le service souhaité
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( 'https://services.odata.org' ).
lo_http_client = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
lo_client_proxy = /iwbep/cl_cp_factory_remote=>create_v4_remote_proxy(
EXPORTING
is_proxy_model_key = VALUE #( repository_id = 'DEFAULT'
proxy_model_id = 'ZTEST_NORTHWIND'
proxy_model_version = '0001' )
io_http_client = lo_http_client
iv_relative_service_root = '/v4/northwind/northwind.svc' ).
ASSERT lo_http_client IS BOUND.
2/ Choisir le produit que l’on souhaite sélectionner et préciser au niveau du proxy l’entity qui sera appelée (ici /Products en précisant « PRODUCTS »)
" Set entity key
ls_entity_key = VALUE #(
product_id = 1 ).
" Navigate to the resource
lo_resource = lo_client_proxy->create_resource_for_entity_set( 'PRODUCTS' )->navigate_with_key( ls_entity_key ).
3/ Executer l’appel API GET vers le service /Products pour le product_id = 1
lo_response = lo_resource->create_request_for_read( )->execute( ).
lo_response->get_business_data( IMPORTING es_business_data = ls_business_data ).
Les données de retour de l’API seront stockées dans la variable ls_business_data qui est du type ls_business_data TYPE ztest_northwind=>tys_alphabetical_list_of_produ (type créé automatiquement par le Service Consumption Model).
En lançant le debug sur cette classe, on voit d’ailleurs le retour de l’appel API
Qui correspond aux données qu’on obtient lorsqu’on appelle l’URL suivante dans le navigateur : https://services.odata.org/v4/northwind/northwind.svc/Products(1)
Code complet
CLASS ztest_crt_proxy DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ztest_crt_proxy IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
ls_entity_key TYPE ztest_northwind=>tys_alphabetical_list_of_produ,
ls_business_data TYPE ztest_northwind=>tys_alphabetical_list_of_produ,
lo_http_client TYPE REF TO if_web_http_client,
lo_resource TYPE REF TO /iwbep/if_cp_resource_entity,
lo_client_proxy TYPE REF TO /iwbep/if_cp_client_proxy,
lo_request TYPE REF TO /iwbep/if_cp_request_read,
lo_response TYPE REF TO /iwbep/if_cp_response_read.
TRY.
" Create http client
*DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement(
* comm_scenario = '<Comm Scenario>'
* comm_system_id = '<Comm System Id>'
* service_id = '<Service Id>' ).
*lo_http_client = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
DATA(lo_destination) = cl_http_destination_provider=>create_by_url( 'https://services.odata.org' ).
lo_http_client = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).
lo_client_proxy = /iwbep/cl_cp_factory_remote=>create_v4_remote_proxy(
EXPORTING
is_proxy_model_key = VALUE #( repository_id = 'DEFAULT'
proxy_model_id = 'ZTEST_NORTHWIND'
proxy_model_version = '0001' )
io_http_client = lo_http_client
iv_relative_service_root = '/v4/northwind/northwind.svc' ).
ASSERT lo_http_client IS BOUND.
" Set entity key
ls_entity_key = VALUE #(
product_id = 1 ).
" Navigate to the resource
lo_resource = lo_client_proxy->create_resource_for_entity_set( 'PRODUCTS' )->navigate_with_key( ls_entity_key ).
" Execute the request and retrieve the business data
lo_response = lo_resource->create_request_for_read( )->execute( ).
lo_response->get_business_data( IMPORTING es_business_data = ls_business_data ).
CATCH /iwbep/cx_cp_remote INTO DATA(lx_remote).
" Handle remote Exception
" It contains details about the problems of your http(s) connection
CATCH /iwbep/cx_gateway INTO DATA(lx_gateway).
" Handle Exception
CATCH cx_web_http_client_error INTO DATA(lx_web_http_client_error).
" Handle Exception
RAISE SHORTDUMP lx_web_http_client_error.
ENDTRY.
ENDMETHOD.
ENDCLASS.
Conclusion
En utilisant le Service Consumption Model, l’appel à des APIs externes est fortement simplifié puisqu’un Proxy est créé en automatique et permet de gérer facilement les appels et leurs retours.
Ce proxy peut-être utilisé pour faire des appels API dans des classes ABAP ou dans des objets RAP avec des customs CDS Entities et l’exposer dans une tuile Fiori par exemple.