Application Development and Automation Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 

This article will help you to set up and execute HTTPS calls to API endpoints, in this case Collibra, but it can work for other connections too.

The request was to setup a connection to Collibra, and to call an endpoint with a file attachment to create assets, then calling another endpoint with a parameter to finalize the result.

Steps:

  1. Setup the connection
  2. Create the client object
  3. Calling an endpoint
  4. Send the command and retrieve the response
  5. Calling an endpoint with parameters
  6. Adding a file as attachment with parameters
  7. Storing the result
  1. Setup the connection
    We used SM59 to setup a connection to Collibra, this has the advantage that usernames/passwords need not to be stored in the code or in parameters.
  2. Create the client object
    We used a global for this:

 

CLASS-DATA  obj_http_client TYPE REF TO if_http_client.
CONSTANTS   co_destination TYPE c LENGTH 60 VALUE 'COLLIBRA CONNECTION'.

 

And used a separate class method to create the connection:

 

 

CALL METHOD cl_http_client=>create_by_destination
      EXPORTING
        destination        = co_destination
      IMPORTING
        client             = obj_http_client
      EXCEPTIONS
        argument_not_found = 1
        plugin_not_active  = 2
        internal_error     = 3
        OTHERS             = 4.
     “ Set the properties, watch out for the protocol version, also used in SM59 setup
    obj_http_client->propertytype_accept_cookie = if_http_client=>co_enabled.
    obj_http_client->request->set_version( if_http_request=>co_protocol_version_1_1 ).
    obj_http_client->propertytype_logon_popup = if_http_client=>co_disabled.

3. Calling an endpoint
We used separate methods for each endpoint, because the call usually differs.
We are also using a prefix here, which is globally declared.

 

 

CONSTANTS   co_prefix       TYPE text60 VALUE '/rest/2.0'.

And in the method we use:

“Construct uri
lv_uri = co_prefix && '/import/results/' && [Insert Job ID here]  && '/summary'.
“Attach to client
cl_http_utility=>set_request_uri( request = obj_http_client->request uri = lv_uri ).
“See endpoint documentation regarding GET/POST etc
obj_http_client->request->set_method('GET').

 

4. Now we send out the command and capture the result, of course this is also put in a separate method:

 

    CALL METHOD obj_http_client->send
      EXPORTING
        timeout                    = 1200
      EXCEPTIONS
        http_communication_failure = 1
        http_invalid_state         = 2
        http_processing_failed     = 3.

“Communication errors can be called this way:
    IF sy-subrc <> 0.
      CALL METHOD obj_http_client->get_last_error(
        IMPORTING
          code    = gv_subrc
          message = gv_errortext ).

      WRITE: / 'communication_error( send )',
            / 'code: ', gv_subrc, 'message: ', gv_errortext.
      gv_error = abap_true.
      EXIT.
    ENDIF.
* Receive Result, the get_cdata will get the actual response!
    CALL METHOD obj_http_client->receive
      EXCEPTIONS
        http_communication_failure = 1
        http_invalid_state         = 2
        http_processing_failed     = 3.

    IF sy-subrc <> 0.
      CALL METHOD obj_http_client->get_last_error(
        IMPORTING
          code    = gv_subrc
          message = gv_errortext ).

      WRITE: / 'communication_error( receive )',
            / 'code: ', gv_subrc, 'message: ', gv_errortext.
      gv_error = abap_true.
      EXIT.
    ENDIF.
“ Response header fields can be extracted using 
“ obj_http_client->response->get_header_fields( CHANGING fields = lt_fields ).
 " Response body is a JSON structure....
 lv_resp = obj_http_client->response->get_cdata( ).

 

5. Calling an endpoint with parameters.
Look at the ’Content_type’ that the endpoint expects!
In case of ‘application/json’ it is easy:

 

    obj_http_client->request->set_header_field( name = 'Content-Type' value = `application/json` ).
    obj_http_client->request->set_cdata( data = `{ "tagNames": [ "Hello" ] }` ).

 

In case of multipart/form data it gets more tricky. The set_cdata still can be used, but the body of the request needs to be constructed. The key to success was to press the “Code” button in Postman. This gave the solution:

 

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="finalizationStrategy"

REMOVE_RESOURCES
------WebKitFormBoundary7MA4YWxkTrZu0gW--

 

The first part is the request header, followed by the request body.
In code, it looks like this:

 

    cl_http_utility=>set_request_uri( request = obj_http_client->request uri = lv_uri ).
    obj_http_client->request->set_method('POST').
    obj_http_client->request->set_header_field( name = 'Content-Type' value = `multipart/form-data; boundary=----WebKitFormROBndary7MA4YWxkTrZu0gW` ).

    lv_request_body = `Content-Type: multipart/form-data; boundary=----WebKitFormROBndary7MA4YWxkTrZu0gW` && cl_abap_char_utilities=>cr_lf &&
                      cl_abap_char_utilities=>cr_lf &&
                      `------WebKitFormROBndary7MA4YWxkTrZu0gW` && cl_abap_char_utilities=>cr_lf &&
                      `Content-Disposition: form-data; name="finalizationStrategy"` && cl_abap_char_utilities=>cr_lf &&
                      cl_abap_char_utilities=>cr_lf &&
                      `REMOVE_RESOURCES` && cl_abap_char_utilities=>cr_lf &&
                      `------WebKitFormROBndary7MA4YWxkTrZu0gW--`.

*    " Set the request body
    obj_http_client->request->set_cdata( data = lv_request_body ).
“ Now send, get response etc

 

The boundary string needs to be a unique string, Postman will regenerate this with each call, I am using the same string over and over.

6. Adding a file as attachment with parameters.
What we need to do here is to add a multipart. Body parameters are set in the header of the multipart:

 

    DATA: lo_part TYPE REF TO if_http_entity.
    DATA: lv_raw TYPE xstring.

    obj_http_client->request->if_http_entity~set_content_type( content_type = 'multipart/form-data'  ).

    obj_http_client->request->set_method('POST').
    lo_part = obj_http_client->request->if_http_entity~add_multipart( ).
“  Set body parameters, actually stored via de header...
    lo_part->set_header_field( name = 'Content-Disposition' value = 'form-data; name="file"; filename="assets.json"' ).

    OPEN DATASET p_file FOR INPUT IN LEGACY BINARY MODE CODE PAGE '1100'.
    IF sy-subrc = 0.
      READ DATASET p_file INTO lv_raw.
      IF sy-subrc = 0.
        CALL METHOD lo_part->append_data
          EXPORTING
            data = lv_raw.
      ENDIF.
    ENDIF.
    CLOSE DATASET p_file.
    lo_part->set_data( data = lv_raw ).

    lo_part->set_formfield_encoding( formfield_encoding = obj_http_client->request->if_http_entity~co_formfield_encoding_raw ).
“ Now send, get response etc.

 

7. Storing the result:
I have created a separate table (BW ADSO in this case) to store the data, using the current time stamp and a row number.
The JSON output is to be stored in name/value fields, to be able to do this, the JSON was converted using this code, which will return a simple table that can be LOOP’ed over

 

 

DATA: lv_xstring TYPE xstring,
          lt_return  TYPE STANDARD TABLE OF bapiret2,
          ls_return  TYPE bapiret2,
          lv_xml     TYPE string.

    DATA(reader) = cl_sxml_string_reader=>create(
                     cl_abap_codepage=>convert_to( i_json ) ).
    DATA(writer) = cl_sxml_string_writer=>create(
                     type = if_sxml=>co_xt_xml10 ).

    reader->next_node( ).
    reader->skip_node( writer ).
    lv_xml = cl_abap_codepage=>convert_from( CAST cl_sxml_string_writer( writer )->get_output( ) ).
    lv_xstring = cl_abap_codepage=>convert_to( source = lv_xml codepage = `UTF-8` ).

    CALL FUNCTION 'SMUM_XML_PARSE'
      EXPORTING
        xml_input = lv_xstring
      TABLES
        xml_table = e_xml_table
        return    = lt_return
      EXCEPTIONS
        OTHERS    = 0.

 

 

That's all, hope you found it interesting/helpful.