Cloud, data 3rd party, json… On vous explique comment récupérer les statistiques de l’épidémie COVID-19 dans votre datawarehouse favori (BW/4HANA)

Il n’est pas rare lors de nos interventions client d’être confronté à des paysages applicatifs comprenant un mix entre données on-premise et données cloud.

Sur un projet Analytics il se peut que l’on vous demande d’industrialiser des chargements d’une application tierce “exotique” hostée dans le cloud vers votre BW/4HANA.

Se pose alors la question de la connectivité pour récupérer ces données. Comment cracker le point sans mettre en place une usine à gaz alors que vous voulez juste remonter 100 lignes tous les jours ?

Le casse tête commence.

“J’aurais jamais dû me mettre là dedans”

Un univers de possibilités… et un univers de complications

Très rapidement vos recherches vous amènerons vers SDI (Smart Data Integration) et SDA (Smart Data Access), deux briques applicatives HANA qui facilitent l’ingestion de données via des adapters. Ceux-ci permettent de se connecter à des bases Microsoft SQL Server, Oracle, MySql, Amazon Redshift… Certains adapters proposent même des fonctionnalités temps réel via analyse de logs. Des éditeurs spécialisés proposent également des connecteurs spécifiques dédiés à des solutions phares du marché (exemple : connecteur SDI pour SalesForce).

Deuxième résultant dans Google, les ETL. Inutile de vous faire un dessin, vous penserez directement à SAP Data Services, Informatica, Talend…

Dans ces deux cas, vous devrez passer par un processus long si ces briques applicatives ne sont pas présentes ou pas configurées dans votre paysage système :

  • Mise en place de serveurs prod & non prod (pour les ETL et SDI)
  • Acquisition de licences potentiellement
  • Configuration/Administration
  • Maintenance de ces nouvelles briques

Vous avez donc l’illumination : le fichier plat ! Vous revenez vite à la raison… il faudra générer un fichier, le déplacer de votre application tierce vers votre serveur applicatif BW/4HANA, mettre en place les mécaniques de chargement… Compliqué !

A la cantine (dans le monde d’avant) vous évoquez la problématique avec votre ami Gerard, développeur ABAP. Il vous propose de passer par les REST API si votre solution tierce en expose une. C’est plié !

Les REST API quesaco ?

Une REST API est un catalogue de ressources exposées par un système et que l’on peut solliciter via le protocole HTTP. Ainsi, on pourra interagir avec ces ressources via différentes méthodes :

  • GET : pour récupérer des listes d’éléments
  • POST : pour insérer un élément
  • PUT : pour modifier des éléments
  • DELETE : pour supprimer des éléments

Avec BW/4HANA dans le décors, voici l’architecture cible de la solution :

Quelques exemples de documentations de REST API :

Attention, certaines API sont monétisées c’est à dire qu’elles sont limitées en API calls (requêtes). Ce point est à voir au cas par cas.

Un bon outil pour tester vos requêtes HTTP est POSTMAN et permet avant le passage à l’implémentation de tester les ressources de l’API que vous souhaitez utiliser.

Passons maintenant à la mise en oeuvre.

Un peu d’ABAP de derrière les fagots sur votre BW/4HANA

Vous avez pu voir fleurir sur linkedin un nombre impressionnant de dashboard mettant en valeur les données du Covid-19. Cependant peu d’entre eux impliquaient une remontée automatique des données.

Afin de stocker les données de statistiques du Covid-19 dans BW/4HANA de façon automatique, il suffit d’utiliser une API mettant à disposition les statistiques. Exemple : https://rapidapi.com/Gramzivi/api/covid-19-data

IMPORTANT : avant toute chose, il faudra procéder à l’import du certificat SSL dans la transaction STRUST afin de sécuriser la connexion.

Voici un premier exemple simple n’impliquant pas de passage de paramètres et permettant de récupérer la liste des pays ainsi que leurs coordonnées géographiques. La ressource de l’api est getListOfCountries, voici comment l’interroger via une méthode GET en ABAP :

" Description du schema JSON de reception
  TYPES: BEGIN OF root,
           name       TYPE string,
           alpha2code TYPE string,
           alpha3code TYPE string,
           latitude   TYPE string,
           longitude  TYPE string,
         END OF root.

  DATA: lt_json_result TYPE TABLE OF root,
        ls_country      TYPE root,
        lt_countries   TYPE TABLE OF root,
        lo_http_client TYPE REF TO if_http_client,
        lo_rest_client TYPE REF TO cl_rest_http_client,
        lv_url         TYPE        string,
        lv_url2        TYPE        string,
        lo_response    TYPE REF TO     if_rest_entity.


  FREE : lo_http_client, lo_rest_client, lo_response.

  CLEAR lv_url.
  lv_url = 'https://covid-19-data.p.rapidapi.com'.

  cl_http_client=>create_by_url(
    EXPORTING
      url                = lv_url
    IMPORTING
      client             = lo_http_client    " HTTP Client Abstraction
    EXCEPTIONS
      argument_not_found = 1                " Communication Parameters (Host or Service) Not Available
      plugin_not_active  = 2                " HTTP/HTTPS Communication Not Available
      internal_error     = 3                " Internal Error (e.g. name too long)
      OTHERS             = 4
  ).
  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
      WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

* Creation de l'instance REST
  CREATE OBJECT lo_rest_client
    EXPORTING
      io_http_client = lo_http_client.

* Positionnement de la version HTTP
  lo_http_client->request->set_version( if_http_request=>co_protocol_version_1_0 ).
  CHECK lo_http_client IS BOUND AND lo_rest_client IS BOUND.


* Positionnement de l'url complète de la ressource
  CLEAR lv_url2.
  CONCATENATE lv_url '/help/countries' INTO lv_url2.

  cl_http_utility=>set_request_uri(
    EXPORTING
      request = lo_http_client->request    " HTTP Framework (iHTTP) HTTP Request
      uri     = lv_url2                     " URI String (in the Form of /path?query-string)
  ).

* Passage d'informations dans le header de la request
  CALL METHOD lo_rest_client->if_rest_client~set_request_header
    EXPORTING
      iv_name  = 'x-rapidapi-host'
      iv_value = 'covid-19-data.p.rapidapi.com'.

  CALL METHOD lo_rest_client->if_rest_client~set_request_header
    EXPORTING
      iv_name  = 'x-rapidapi-key'
      iv_value = 'b717f95b41msh7af221e6abde397p1e841ejsn893301788286'.


* Typage de la requête HTTP => GET
  lo_rest_client->if_rest_client~get( ).

* Création de la réponse HTTP
  lo_response = lo_rest_client->if_rest_client~get_response_entity( ).

* Récupération du statut de la requête
  DATA(http_status)   = lo_response->get_header_field( '~status_code' ).

* Récupération du resultat au format JSON
  DATA(json_response_str) = lo_response->get_string_data( ).
  DATA(json_response) = lo_response->get_binary_data( ).

* Refresh request
  CALL METHOD lo_rest_client->refresh_request.
  CALL METHOD lo_http_client->close.

*  Deserialisation du résultat JSON.
  /ui2/cl_json=>deserialize( EXPORTING json = json_response_str CHANGING data = lt_json_result ).

  IF lt_json_result IS NOT INITIAL.
    LOOP AT lt_json_result INTO ls_country.
      APPEND ls_country TO lt_countries.
    ENDLOOP.
  ENDIF.

* insérer votre code ici pour écrire dans une table ou un ADSO en Direct Update

On notera plusieurs points importants :

  • La mise en place d’une structure abap (root ici) correspondant au format JSON de la ressource interrogée
  • La déserialisation via la méthode /ui2/cl_json=>deserialize
  • Le parcours d’un set de resultat via la dernière loop
  • Vous pouvez insérer les résultats dans une table ou un ADSO en Direct Update afin d’utiliser les données dans votre modeling par la suite

Voici la table interne lt_countries une fois les résultats désérialisés à partir du JSON :

Enfin, si vous souhaitez récupérer les statistiques par pays, la ressource getDailyReportByCountryName est disponible et permet de récupérer le nombre de cas covid (contaminations, guérisons, décès…) par pays : getDailyReportByCountryName.

Cette ressource utilise elle des paramètres qu’il faut passer dans la requête HTTP :

  • la date des statistiques à récupérer
  • le pays

Lors de l’appel de la ressource il suffira de positionner la ligne suivante au moment de la constitution de l’URL:

CLEAR lv_url2.
CONCATENATE lv_url '/report/country/name?date=2020-06-03&name=France' INTO lv_url2.

Si vous avez stocké les pays, vous pourrez appeler le service en loopant sur chaque pays récupéré lors de l’appel du premier service et modifier l’URL en conséquence.

La structure du format JSON de cette ressource est plus complexe dans ce cas car on est confronté à une deepstructure (le pays, puis ses provinces/territoires). Exemple pour la France :

On voit ici que les données retournées sont de 2 niveaux :

  • En tête avec le pays via l’attribut “country”
  • Lignes avec chaque “province”

Morale de l’histoire

Pas besoin dans ce cas de déployer des usines à gaz ou de modifier votre architecture pour industrialiser un chargement simple de données externes. L’utilisation d’une REST API et quelques lignes d’ABAP feront l’affaire.

La finalité étant de stocker ces données (dans une table ou un ADSO en Direct Update), vous pourrez dans la foulée mixer ces données externes avec vos données SAP par exemple.

Chez Censio nous utilisons ce procédé afin de récupérer des données de Confluence Cloud, modifier des pages Confluence Cloud et récupérer les User Stories Jira Cloud afin de monitorer notre centre de service TMA Analytics & ERP via du reporting SAC.

Laisser un commentaire