Monday, 20 March 2023

Consuming External API’s in ABAP Code Snippets

In this Blog, I would like to present some code snippets, which could help others in order to consume Token based REST APIs in ABAP.

Obtaining the relevant details in order to get the Token

In order to get the Token, details like Application ID, Application Secret, Client ID and the Token Endpoint must be provided by the Provider.

Example how these details looks like is as follows:

SAP ABAP Certification, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Jobs, SAP ABAP Prep, SAP ABAP Preparation, SAP ABAP Certifications

Application ID, Client ID and Secret are simply data strings and the Token Endpoint is a URL.

Code Snippet in order to get a Token

The definition of which header parameters are needed in order to successfully call the Token Endpoint has to be provided by the API Provider.

METHOD get_token.
    DATA: lo_http_token    TYPE REF TO if_http_client,
          l_response_token TYPE string.
    DATA: l_status_code   TYPE i.
    DATA: lv_bas64enc TYPE string.
    DATA: lv_url_token TYPE string,
          lv_client_id TYPE string.
    DATA: lo_root TYPE REF TO cx_root.

    TRY.
*Url to access token
        lv_url_token    = IS_API_CREDENTI-token_endpoint. "Token Endpoint

* create http client Object
        CALL METHOD cl_http_client=>create_by_url
          EXPORTING
            url                = lv_url_token
          IMPORTING
            client             = lo_http_token
          EXCEPTIONS
            argument_not_found = 1
            plugin_not_active  = 2
            internal_error     = 3
            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.
*Close the client in case of Error
          lo_http_token->close( ).
        ENDIF.

* Set Method POST
        IF lo_http_token IS BOUND.
          lo_http_token->request->set_method( if_http_entity=>co_request_method_post ).
        ENDIF.

*Set header parameter
*Convert string to base64
CONCATENATE IS_API_CREDENTI-APPLICATION_ID ':' IS_API_CREDENTI-APPLICATION_SEC INTO  lv_bas64enc .



        cl_http_utility=>encode_base64( EXPORTING
                                    unencoded = lv_bas64enc
                                  RECEIVING
                                    encoded   = lv_bas64enc ).

        CONCATENATE 'Basic ' lv_bas64enc INTO lv_bas64enc RESPECTING BLANKS.

        lo_http_token->request->set_header_field(
           EXPORTING
           name =  'Authorization'
           value =  lv_bas64enc
                                              ).

        lo_http_token->request->set_header_field(
                     EXPORTING
                     name = 'Content-Type'
                     value = 'application/x-www-form-urlencoded'
                                                ).
        lo_http_token->request->set_header_field(
                     EXPORTING
                     name = 'Accept'
                     value = 'application/json'
                                                ).
*Set all the form parameters
        lo_http_token->request->set_form_field(
             EXPORTING
             name = 'grant_type'
             value = 'client_credentials'
                                              ).

        lo_http_token->request->set_form_field(
               EXPORTING
               name = 'scope'
               value = 'client'
                                               ).


        lv_client_id = IS_API_CREDENTI-client_id.
        lo_http_token->request->set_form_field(
              EXPORTING
              name = 'client_id'
              value =  lv_client_id
                                               ).

* Turn off logon popup. detect authentication errors.
        lo_http_token->propertytype_logon_popup = 0.

* Send / Receive Token Request
        CALL METHOD lo_http_token->send
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            http_invalid_timeout       = 4
            OTHERS                     = 5.
        IF sy-subrc <> 0.
*          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
*                     WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        CALL METHOD lo_http_token->receive
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            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.

        CALL METHOD lo_http_token->response->get_status
          IMPORTING
            code = l_status_code.

*Json response needs to be converted to abap readable format
        CALL METHOD lo_http_token->response->get_cdata
          RECEIVING
            data = l_response_token.


        /ui2/cl_json=>deserialize(
              EXPORTING
                  json = l_response_token
              CHANGING
                  data = is_token   "Token will be stored in the instance structure, for retrieval in other methods.
                                 ).

        lo_http_token->close( ).
      CATCH cx_root INTO lo_root.
    ENDTRY.
  ENDMETHOD.

Data is_token is of the following type

    BEGIN OF ts_token,
        token_type   TYPE string,
        access_token TYPE string,
        expires_in   TYPE string,
      END OF ts_token .

Calling an API with PUT operation using the Obtained token including Error Handling

In the following code, An API is called using the Token obtained above in order to write some data on the API Provider side. Various return status codes which could come as return have also been processed.

Details of the Parameters which needs to be provided in order to make a call has to come from the API provider, Also the details about the structure of the error data has to come from the API provider.

METHOD put_employee_call.
    DATA: lv_url_api TYPE string.
    DATA: lo_http_put_empl TYPE REF TO if_http_client.

    DATA: lo_root TYPE REF TO cx_root.

    DATA: lv_access_token TYPE string.
    DATA: l_response_put TYPE string.

    DATA: l_status_code   TYPE i.
    DATA: ls_mitarbeiter_put_resp_422   TYPE ts_put_resp_unprocessable,
          ls_mitarbeiter_put_resp_400_4 TYPE ts_put_resp_400_4.

    DATA: ls_errors_422                   TYPE ts_put_errors,
          ls_msg                          TYPE bal_s_msg,
          lv_text                         TYPE char200,
          ls_emp_put_resp_200_201 TYPE ts_put.

    TRY.


lv_url_api = is_api_credenti-api_endpoint_new .

*Create http client Object
        CALL METHOD cl_http_client=>create_by_url
          EXPORTING
            url                = lv_url_api
          IMPORTING
            client             = lo_http_put_empl
          EXCEPTIONS
            argument_not_found = 1
            plugin_not_active  = 2
            internal_error     = 3
            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.
*Close the client in case of Error
          lo_http_put_empl->close( ).
        ENDIF.
*Set Method POST
        IF lo_http_put_empl IS BOUND.
          lo_http_put_empl->request->set_method( 'PUT' ).
        ENDIF.

*Set Header fields
        lo_http_put_empl->request->set_header_field(
                     EXPORTING
                     name = 'Accept'
                     value = 'application/json'
                                                ).

        lo_http_put_empl->request->set_header_field(
                     EXPORTING
                     name = 'Content-Type'
                     value = 'application/json'
                                                ).

        CONCATENATE is_token-token_type  is_token-access_token INTO  lv_access_token SEPARATED BY space.

        lo_http_put_empl->request->set_header_field(
         EXPORTING
         name =  'Authorization'
         value = lv_access_token
                                               ).
*Set body Parameter
        lo_http_put_empl->request->append_cdata( data = iv_empl_json ).
* Turn off logon popup. detect authentication errors.
        lo_http_put_empl->propertytype_logon_popup = 0.

*Send / Receive PUT Request
        CALL METHOD lo_http_put_empl->send
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            http_invalid_timeout       = 4
            OTHERS                     = 5.
        IF sy-subrc <> 0.
*            MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
*                       WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
        ENDIF.

        CALL METHOD lo_http_put_empl->receive
          EXCEPTIONS
            http_communication_failure = 1
            http_invalid_state         = 2
            http_processing_failed     = 3
            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.

*Get status code
        CALL METHOD lo_http_put_empl->response->get_status
          IMPORTING
            code = l_status_code.

*Obtain Json response
        CALL METHOD lo_http_put_empl->response->get_cdata
          RECEIVING
            data = l_response_put.

*deserializing json response
        CASE l_status_code.
          WHEN '422' . " Unprocessable Entry
            /ui2/cl_json=>deserialize(
                          EXPORTING
                              json = l_response_put
                          CHANGING
                              data = ls_mitarbeiter_put_resp_422
                                             ).
*Add log for every Error Entry
            LOOP AT ls_mitarbeiter_put_resp_422-errors INTO ls_errors_422.
              CONCATENATE ls_errors_422-field  ls_errors_422-code ls_errors_422-message INTO lv_text SEPARATED BY space.
              me->return_log_variables( EXPORTING iv_text = lv_text CHANGING cs_msg = ls_msg ).
              ls_msg-msgty     = 'E'.
              ls_msg-msgid     = const_freetext_msgid.
              ls_msg-msgno     = const_freetext_msgno.
              me->add_log_record( is_msg = ls_msg ).
              CLEAR: ls_msg,ls_errors_422,lv_text.
            ENDLOOP.

          WHEN '400' OR '404'.
            /ui2/cl_json=>deserialize(
                        EXPORTING
                            json = l_response_put
                        CHANGING
                            data = ls_mitarbeiter_put_resp_400_4
                                           ).
*Add one log Entry for 400 or 404
            CONCATENATE  ls_mitarbeiter_put_resp_400_4-message ls_mitarbeiter_put_resp_400_4-code INTO lv_text SEPARATED BY space.
            me->return_log_variables( EXPORTING iv_text = lv_text CHANGING cs_msg = ls_msg ).
            ls_msg-msgty     = 'E'.
            ls_msg-msgid     = const_freetext_msgid.
            ls_msg-msgno     = const_freetext_msgno.
            me->add_log_record( is_msg = ls_msg ).
            CLEAR: ls_msg,lv_text.

*Add one Success entry for Update
          WHEN '200'.
            /ui2/cl_json=>deserialize(
                    EXPORTING
                        json = l_response_put
                    CHANGING
                        data = ls_emp_put_resp_200_201
                                       ).

            CONCATENATE ls_emp_put_resp_200_201-external_id  TEXT-002  INTO lv_text SEPARATED BY space.
            me->return_log_variables( EXPORTING iv_text = lv_text CHANGING cs_msg = ls_msg ).
            ls_msg-msgty     = 'S'.
            ls_msg-msgid     = const_freetext_msgid.
            ls_msg-msgno     = const_freetext_msgno.
            me->add_log_record( is_msg = ls_msg ).
            CLEAR: ls_msg,lv_text.

*Add one Success entry for Creation
          WHEN '201'.
            /ui2/cl_json=>deserialize(
                       EXPORTING
                           json = l_response_put
                       CHANGING
                           data = ls_emp_put_resp_200_201
                                          ).
            CONCATENATE ls_emp_put_resp_200_201-external_id TEXT-003  INTO lv_text SEPARATED BY space.
            me->return_log_variables( EXPORTING iv_text = lv_text CHANGING cs_msg = ls_msg ).
            ls_msg-msgty     = 'S'.
            ls_msg-msgid     = const_freetext_msgid.
            ls_msg-msgno     = const_freetext_msgno.
            me->add_log_record( is_msg = ls_msg ).
            CLEAR: ls_msg,lv_text.
        ENDCASE.
        lo_http_put_empl->close( ).
      CATCH cx_root INTO lo_root.
    ENDTRY.
  ENDMETHOD.

No comments:

Post a Comment