Upload data from file into Z table

With the coding below you can use a file to upload data into any Z table. The program below first deletes the full content first, then upload the content from the file. If you want different behavior, you can adjust the program to your own needs.

The program is to be protected with a proper authorization check. And it is for utility support only for non-productive systems. Do not use on productive systems.

Be careful: the program first deletes ALL the content of the current table. Then it inserts the entries from the file.

Program selection screen:

Coding:

*&--------------------------------------------------------------------*
*& Report Z_UPLOAD_TABLE
*&--------------------------------------------------------------------*
*& Description: Upload the data from a file and fill a Z table
*&--------------------------------------------------------------------*

REPORT z_upload_table.

PARAMETERS:
  p_table TYPE dd02l-tabname OBLIGATORY,
  p_file  TYPE ibipparms-path OBLIGATORY.

DATA: lt_file_data  TYPE STANDARD TABLE OF string,
      lt_table_data TYPE REF TO data,
      lt_fieldcat   TYPE lvc_t_fcat,
      lt_component  TYPE abap_component_tab,
      lv_separator  TYPE c LENGTH 1 VALUE ','.
DATA: lv_offset TYPE i VALUE 0,
      lv_until  TYPE i  VALUE 0,
      lv_field  TYPE string.
DATA: lv_filename TYPE string.

FIELD-SYMBOLS: <lt_table_data> TYPE STANDARD TABLE,
               <ls_table_data> TYPE any,
               <lv_field>      TYPE any.

DATA: new_line  TYPE REF TO data.

AT SELECTION-SCREEN.
  DATA: l_got_state TYPE  ddgotstate.
* Validate table name
  CALL FUNCTION 'DDIF_TABL_GET'
    EXPORTING
      name     = p_table
    IMPORTING
      gotstate = l_got_state
    EXCEPTIONS
      OTHERS   = 1.
  IF l_got_state <> 'A'.
    MESSAGE 'Table does not exist' TYPE 'E'.
  ENDIF.
  IF p_table+0(1) <> 'Z' AND
     p_table+0(1) <> 'Y'.
    MESSAGE 'Please use only Z or Y tables.' TYPE 'E'.
  ENDIF.

*----------------------------------------------------------------------*
*     AT SELECTION-SCREEN ON VALUE-REQUEST
*----------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.

  CALL FUNCTION 'F4_FILENAME'
    IMPORTING
      file_name = p_file.

START-OF-SELECTION.

* Dynamically create internal table
  CREATE DATA lt_table_data TYPE TABLE OF (p_table).
  ASSIGN lt_table_data->* TO <lt_table_data>.

  CREATE DATA new_line LIKE LINE OF <lt_table_data>.
  ASSIGN new_line->* TO <ls_table_data>.

* Generate field catalog for the table
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name = p_table
    CHANGING
      ct_fieldcat      = lt_fieldcat
    EXCEPTIONS
      OTHERS           = 1.
  IF sy-subrc <> 0.
    MESSAGE 'Error generating field catalog' TYPE 'E'.
  ENDIF.

  lv_filename = p_file.
* Read file into internal table
  CALL FUNCTION 'GUI_UPLOAD'
    EXPORTING
      filename                = lv_filename
      filetype                = 'ASC'
    TABLES
      data_tab                = lt_file_data
    EXCEPTIONS
      file_open_error         = 1
      file_read_error         = 2
      no_batch                = 3
      gui_refuse_filetransfer = 4
      OTHERS                  = 5.
  IF sy-subrc <> 0.
    MESSAGE 'Error uploading file' TYPE 'E'.
  ENDIF.

* Delete all entries from the target table
  DELETE FROM (p_table).
  IF sy-subrc = 0.
    MESSAGE 'All entries deleted from table' TYPE 'I'.
  ENDIF.

* Parse and insert data into the table

  DESCRIBE TABLE lt_file_data LINES DATA(lv_idx).
  DELETE lt_file_data INDEX lv_idx.

  LOOP AT lt_file_data INTO DATA(ls_line) FROM 4.
    CLEAR <ls_table_data>.

    LOOP AT lt_fieldcat ASSIGNING FIELD-SYMBOL(<fs_fieldcat>).
      IF <fs_fieldcat>-fieldname = 'MANDT'.
        ASSIGN COMPONENT <fs_fieldcat>-fieldname OF STRUCTURE <ls_table_data> TO <lv_field>.
        IF sy-subrc = 0.
          <lv_field> = sy-mandt.
        ENDIF.
      ELSE.
        CLEAR lv_offset.
        DO strlen( ls_line ) TIMES.
          DATA(lv_index) = sy-index.

          DATA(lv_single) = substring( val = ls_line off = lv_index - 1 len = 1 ).
          IF lv_index = 1 AND lv_single = '|'.
            lv_offset = lv_offset + 1.
          ELSEIF lv_single = '|'.   "New field
            lv_until = lv_index - lv_offset - 1.
            lv_field = ls_line+lv_offset(lv_until).
            lv_offset = lv_offset + lv_until + 1.

            ASSIGN COMPONENT <fs_fieldcat>-fieldname OF STRUCTURE <ls_table_data> TO <lv_field>.
            IF sy-subrc = 0.
              <lv_field> = lv_field.
            ENDIF.
            ls_line = ls_line+lv_offset.
            EXIT.
          ENDIF.
        ENDDO.
      ENDIF.
    ENDLOOP.

    APPEND <ls_table_data> TO <lt_table_data>.

  ENDLOOP.

* Insert data into the database table
  INSERT (p_table) FROM TABLE <lt_table_data>.
  IF sy-subrc = 0.
    MESSAGE 'Data successfully inserted into table' TYPE 'S'.
  ELSE.
    MESSAGE 'Error inserting data into table' TYPE 'E'.
  ENDIF.

SE39 ABAP split screen editor

The SE39 transaction is used to compare two ABAP’s in split screen mode. This can be two ABAP programs in single system, or by clicking on the button Compare Different Systems between two systems. With the cross system comparison the tool will ask for which RFC to use.

Usage of SE39

Start transaction SE39. In this example we will take a copy of a standard SAP BAPI and compare it with the original (to find clones, read this blog):

Press display to compare. Initially not much happens, except source code on the left and right.

Press the Comparison On button:

Result now shows the delta’s with indicators on the left side of the screen:

Use the buttons Next difference from cursor and the other button to quick jump through the code to find the differences.

References

Bug fix notes:

3539141 – Splitscreeneditor: Error for empty source in version management

3564002 – SE39: improve handling of inactive programs

3568037 – Enhancements are not being displayed in SE39 for remote systems

XPRA transport actions

XPRA actions can be a must to to trigger actions after import. The most know is to trigger the RV80HGEN program jointly with update of requirement or formula.

The XPRA can also be misused by hackers to execute actions right after transport import. Since XPRA is done with the DDIC user upon import, it will take the rights from DDIC as well. Very often DDIC has SAP_ALL assigned.

Correct usage of XPRA

To add a XPRA action, go to the transport via SE10 or SE01 and press change. Then use the Insert Row button to manually add the XPRA action:

Add program ID R3TR, object type XPRA and object name the program to be executed. In this case the RV80GHEN program.

Now save the transport.

Upon import the program will be executed at the end of the import. This can be seen in the transport import log details:

Other XPRA programs

Next to RV80GHEN there are more known XPRA programs:

2400599 – List of Programs to regenerate CUSTOM fields after upgrade or patch update lists RGUGBR02, SAPFACCG and RFBIBLG0.

1365712 – Generating FI programs during posting after importing an SP lists program RGZZGLUX to regenerate the finance programs.

1855686 – Dump in XPRA phase because of FAGL_VIEWS_GENERATE_ALL lists program SAPLFAGL_VIEWS_GENERATE (and old program SAPFACCG).

Misuse of XPRA

Off course any developer can add a malicious XPRA statement to execute a program upon import.

What to do about this?

Prevention is not possible. Detection is possible. See the blog on adding a critical object check to the transport check tool. In this case add a check on R3TR XPRA and program *. This will detect any XPRA object.

Webdynpro tips & tricks

SAP web dynpro is still in use and is a good way for ABAP devevloper to create web screens for end users.

Tips and tricks for basis

Transactions:

  • SICF: activation or de-activation of web dynpro services (see blog for all SICF tips and tricks)
  • SICF_SESSIONS: Enable HTTP security session management at backend(ABAP) system
  • SMGW: gateway monitor
  • SMICM: HTTPs settings

General:

SAP help documentation on WebDynpro can be found here (very extensive).

Unified rendering

Web dynpro uses the unified rendering framework. This needs regular patching. See this blog for more information.

SCOV coverage analyzer

The SAP coverage analyze can be used in testing custom code to make sure all testing covers all code and all branches.

The coverage analyzer has quite an in-depth analysis capability that is meant for non-productive systems. Don’t run it on productive systems.

Starting the coverage analyzer

On your test system start transaction SCOV and go to the on/off section in the Administration branch:

When switched on it will take at least 1 hour before the first results are visible.

Analyzing the results

During the testing you can view the test coverage results in the Display/Details section. Select there the Z* for all the custom code (you might need to increase the Maximum Number of Objects in the Settings tab):

Wait for the result list and sort by Coverage:

Double clicking on one of the lines gives the coverage per program:

If you select any line you jump to the code lines:

In this case (yellow one), you can see that the normal flow is done, but the exceptions are not reached yet. If the exceptions are important for your business scenario, you know you have to create and fire a test case. Once done, the coverage should increase.

Overall view

In the overall view you can see trend analysis over time:

To refresh the analysis or to set up first time push the Update Analysis button in the middle section (it is a bit hidden).

References

Standard SAP reference for Coverage analyzer: link.

OSS notes:

ABAP dictionary check

In some cases you need to check the entire system for inactive objects.

For this you can use program RUTMSJOB (transaction S_SLN_44000018):

Select the checks and schedule the jobs with the button.

With the job overview you can see the running status:

When completed you can use the Results button:

By clicking on the glass icon, you can list the results of items that are not ok.

Other potential programs

Other potential programs are these ones:

DD_REPAIR_TABT_DBPROPERTIES, see OSS notes

RUTCONSCHECK and RUTCHKNT, see OSS note:

OAUTH call from ABAP

OAUTH can be called from custom ABAP. The explanation is given in this formal SAP help file. But it is quite complex.

In the example program below we will use OAUTH to call SAP BTP CPI.

First in SE80 we create a OAUTH client profile named ZOAUTH_CLIENT_PROFILE_CPI:

Then the rest of the ABAP coding is according to the SAP help file, including the error handling on issues you might face.

*&--------------------------------------------------------------------*
*& Report Z_CALL_API_USING_OAUTH
*&--------------------------------------------------------------------*
*&
*&--------------------------------------------------------------------*

REPORT z_call_api_using_oauth.

PARAMETERS:
  zp_url   TYPE string                    LOWER CASE
                                          DEFAULT 'https://apimanagement.eu10.hana.ondemand.com/v1/api/hc/xxx/call_name',
  zp_sslid TYPE strustssl-applic          DEFAULT 'ANONYM',
  zp_profl TYPE oa2c_profiles-profile     DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI',
  zp_confg TYPE oa2c_client-configuration DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI'.

CONSTANTS:
  BEGIN OF zgcs_create_return,
    argument_not_found TYPE sy-subrc VALUE 1,
    plugin_not_active  TYPE sy-subrc VALUE 2,
    internal_error     TYPE sy-subrc VALUE 3,
    others             TYPE sy-subrc VALUE 4,
  END OF zgcs_create_return.

START-OF-SELECTION.

  " oData: restrict to two entries returned, via url
  DATA(zgv_api_url) = |{ zp_url }?$top=2|.

  cl_http_client=>create_by_url( EXPORTING  url                = zgv_api_url
                                            ssl_id             = zp_sslid
                                 IMPORTING  client             = DATA(zlo_http_client)
                                 EXCEPTIONS argument_not_found = zgcs_create_return-argument_not_found
                                            plugin_not_active  = zgcs_create_return-plugin_not_active
                                            internal_error     = zgcs_create_return-internal_error
                                            OTHERS             = zgcs_create_return-others ).

  CASE sy-subrc.
    WHEN zgcs_create_return-argument_not_found.
      MESSAGE 'Argument not found when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-plugin_not_active.
      MESSAGE 'Plugin not active for creation of http client instance' TYPE 'E'.
    WHEN zgcs_create_return-internal_error.
      MESSAGE 'Internal error when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-others.
      MESSAGE 'Generic error when trying to create http client instance' TYPE 'E'.
  ENDCASE.

  zlo_http_client->propertytype_logon_popup = zlo_http_client->co_disabled.

  TRY.
      DATA(zgo_oauth_client) = cl_oauth2_client=>create( i_profile       = zp_profl
                                                         i_configuration = zp_confg ).
    CATCH cx_oa2c_config_not_found.
      MESSAGE 'OAuth 2.0 Client Configuration not found' TYPE 'E'.
    CATCH cx_oa2c_config_profile_assign.
      MESSAGE 'OAuth 2.0 Client Config - Unassigned Profile' TYPE 'E'.
    CATCH cx_oa2c_kernel_too_old.
      MESSAGE 'OAuth 2.0 Client - Kernel too old' TYPE 'E'.
    CATCH cx_oa2c_missing_authorization.
      MESSAGE 'OAuth 2.0 Client missing authorization' TYPE 'E'.
    CATCH cx_oa2c_config_profile_multi.
      MESSAGE 'OAuth 2.0 Client Config - Profile assigned multiple times' TYPE 'E'.
  ENDTRY.

  " Set oAuth token to the http client
  TRY.
      zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                   i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
    CATCH cx_oa2c_at_not_available
          cx_oa2c_at_expired.

      " When setting the token fails, first try and get a new token
      TRY.
          zgo_oauth_client->execute_cc_flow( ).
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_not_allowed.
          MESSAGE 'OAuth 2.0 Client Runtime - Not Allowed' TYPE 'E'.
        CATCH cx_oa2c_prot_http_failure.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP Failure' TYPE 'E'.
        CATCH cx_oa2c_prot_other_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Other Error' TYPE 'E'.
        CATCH cx_oa2c_prot_unexpected_code.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unexpected Code' TYPE 'E'.
        CATCH cx_oa2c_prot_http_forbidden.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 403 - Forbidden' TYPE 'E'.
        CATCH cx_oa2c_prot_http_not_found.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 404 - Not Found' TYPE 'E'.
        CATCH cx_oa2c_server_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Server Error' TYPE 'E'.
        CATCH cx_oa2c_temporarily_unavail.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Temporarily Unavailable' TYPE 'E'.
        CATCH cx_oa2c_unsupported_grant_type.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unsupported Grant Type' TYPE 'E'.
        CATCH cx_oa2c_unauthorized_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unauthorized Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_scope.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Scope' TYPE 'E'.
        CATCH cx_oa2c_invalid_grant.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Grant' TYPE 'E'.
        CATCH cx_oa2c_invalid_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_request.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Request' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_secstore_adm.
          MESSAGE 'OAuth 2.0 Client Runtime - SecStore Administration' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_protocol_exception.
          MESSAGE 'OAuth 2.0 Client Runtime - Protocol Exception' TYPE 'E'.
      ENDTRY.

      " Set oAuth token to the http client
      TRY.
          zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                       i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
        CATCH cx_oa2c_at_not_available.
          MESSAGE 'oAuth 2.0: Acces token not available' TYPE 'E'.
        CATCH cx_oa2c_at_expired.
          MESSAGE 'Access Token has expired.' TYPE 'E'.
        CATCH cx_oa2c_at_profile_not_covered.
          MESSAGE 'Access token has expired.' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_icf_error.
          MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
      ENDTRY.

    CATCH cx_oa2c_at_profile_not_covered.
      MESSAGE 'Access token has expired.' TYPE 'E'.
    CATCH cx_oa2c_not_supported.
      MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
    CATCH cx_oa2c_badi_implementation.
      MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
    CATCH cx_oa2c_secstore.
      MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
    CATCH cx_oa2c_invalid_parameters.
      MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
    CATCH cx_oa2c_icf_error.
      MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
  ENDTRY.

  " From here on handle the http client for the API interaction
  zlo_http_client->request->set_version( if_http_request=>co_protocol_version_1_0 ).
  DATA(zlo_rest_client) = NEW cl_rest_http_client( io_http_client = zlo_http_client ).

" Get data from API
  TRY.
      zlo_rest_client->if_rest_client~get( ).
      " Collect response received from the REST API
      DATA(zli_response) = zlo_rest_client->if_rest_client~get_response_entity( ).
      DATA(zgv_http_status_code) = zli_response->get_header_field( `~status_code` ).
      DATA(zgv_status_reason)    = zli_response->get_header_field( `~status_reason` ).
      DATA(zgv_response_data)    = zli_response->get_string_data( ).

      " Record the response of the interface
      IF zgv_http_status_code BETWEEN 200 AND 299.
        " Success
        MESSAGE 'Call was succesful' TYPE 'S'.
      ELSE.
        MESSAGE 'Call failed' TYPE 'E'.
      ENDIF.

      WRITE / 'Response'.
      WRITE / zgv_response_data.

      " Issues with REST client must not lead to a short-dump
    CATCH cx_rest_client_exception INTO DATA(zlx_rest_client).
      IF zlx_rest_client->if_t100_message~t100key IS NOT INITIAL.
        DATA zlv_message TYPE string.
        MESSAGE ID zlx_rest_client->if_t100_message~t100key-msgid
                 TYPE 'E'
                 NUMBER zlx_rest_client->if_t100_message~t100key-msgno
                   WITH zlx_rest_client->if_t100_message~t100key-attr1
                        zlx_rest_client->if_t100_message~t100key-attr2
                        zlx_rest_client->if_t100_message~t100key-attr3
                        zlx_rest_client->if_t100_message~t100key-attr4.
      ELSE.
        MESSAGE 'Rest client Exception' TYPE 'E'.
      ENDIF.
  ENDTRY.

  zlo_http_client->close( ).

Security Services Tools

SAP offers on GitHub some extra Security Service Tools. These are custom Z ABAPs you can download and modify to your needs.

Link to GitHub:

GitHub – SAP-samples/security-services-tools: If you use security-related services and tools such as EWA, SOS, System Recommendations, Configuration Validation, or a security dashboard in SAP Solution Manager, the ABAP reports in this repository can help with further analysis and development.

Interesting programs from Security Service Tools

Some highlights from the Security Service Tools page:

  • Extensive cleanup program for weak hashes (including the password history data)
  • Workload statistics of RFC calls
  • Show SNC status of active users on application server
  • Show RFC gateway and logging settings
  • History of dynamic profile parameters
  • ….
  • Many more

SICF tips and trikcs

SICF is an abbreviation for SAP internet communication framework.

It is used to expose internet services like SAP ABAP webdynpro, ODATA etc.

Checking active services

As per SAP “Note 1555208 – ICF services become inactive after upgrade or SP update” you can find the list of active services with the report RS_ICF_SERV_ADMIN_TASKS (choose option Export of Active Services into CSV file).

On table level: Check the table ICFSERVLOC. All active services are marked with an “X” flag.

Checking SICF security settings

Don’t use the old program RSICFCHK (see OSS note 3300857 – Report RSICFCHK shows incomplete result). Use the new SECSTORE transaction. At the start of transaction SECSTORE choose in the check entries section “ICF Service”:

Now hit execute and check the results:

Mass processing

SICF mass processing is done via program RS_ICF_SERV_MASS_PROCESSING.

Logging of SICF changes

To enable logging of SICF changes: switch on table logging for table ICFSERVLOC.

Various OSS notes around SICF

User based debugging

In some cases you need to debug the session of another user. This can be needed for example when you need to solve an issue in ABAP for a FIORI app. The end user is doing his work until the break point is reached. Then you take over the session using the normal debugging tools. The basic principle is explained in OSS note 1919888 – Debugging the applications of another user, and in this SAP help file.

Prerequisites:

Then let the user start the work. You will take over as soon as the break point is reached.

Checklist for issues can be found in OSS note 2462481 – External debug / breakpoint is not recognized.

Set the user ID to be debugged

For your user ID choose the menu option Utilities/Settings. Then select main tab ABAP editor and subtab Debugging:

Now replace your user with the user name for which you want to take the session over using the external break point.