Skip to Content

Introduction

This blog will guide you through the process of uploading a GOS attachment via a SICF service (REST). This blog will only feature the essentials. So, no error handling or anything of that sort. It will give you information on how to create attachments for notifications, but could be extended for other objects rather easily!

 

Following steps will be taken to create and test such a service:

  1. Creating the web service in SAP Backend
    1. Creating the ZIF_REST Interface.
    2. Creating a Handler Class
    3. Creating the class for a POST Request
    4. Creating a node in transaction SICF
  2. Creating the SAPUI5 frontend
    1. Creating a new project
  3. Testing the service with SAPUI5 frontend

 

1.    Creating the web service in SAP Backend

1.1.    Creating the ZIF_REST Interface.

To start off with, let’s create an interface in ‘SE24’ (if it doesn’t exist yet) which we will use for almost all classes in this guide. The interface consists of two methods, one for the request and one for the response.

 

Add the attributes ‘RESPONSE‘ and ‘REQUEST‘ to the interface.

 

The method ‘SET_RESPONSE’ has an importing parameter ‘IS_DATA’ with the ‘XSTRING’ type. Make sure you activate the interface.

 

1.2.    Creating a Handler Class

Go to ‘SE24’ and create a new class called ‘ZCL_TEST_GOS_ATTACHMENT. Select the tab ‘Interfaces’ and add interface ‘IF_HTTP_EXTENSION’.

 

Go back to the tab ‘Methods’ and you’ll see a method has been added. Add another method called ‘GET_REST’.

 

Add the following parameters to the method ‘GET_REST’.

 

Copy the following code to your method ‘IF_HTTP_EXTENSION~HANDLE_REQUEST’.

It will execute the ‘GET_REST’ method to get the class we want to execute to get or process the data.

METHOD IF_HTTP_EXTENSION~HANDLE_REQUEST.

***************************************************************************
" VARIABLES
***************************************************************************
DATA: LO_REST_CLASS     TYPE REF TO ZIF_REST.
DATA: LV_REASON         TYPE STRING.
DATA: LO_ERROR          TYPE REF TO CX_ROOT.

***************************************************************************
" GET THE CLASS WE WANT TO EXECUTE
***************************************************************************
TRY.

LO_REST_CLASS ?= GET_REST( IO_SERVER = SERVER ).

***************************************************************************
" EXECUTE THE CLASS
***************************************************************************
LO_REST->HANDLE_REQUEST( ).

***************************************************************************
" CATCH ERRORS
***************************************************************************
CATCH CX_ROOT INTO LO_ERROR.

LV_REASON = LO_ERROR->GET_TEXT( ).
SERVER->RESPONSE->SET_STATUS( CODE = 500   
REASON = LV_REASON ).

ENDTRY.

ENDMETHOD.​

 

This method will first check what type of request we’re dealing with (GET, PUT, POST, … etc.).  It will then append the name to the base class name. So, if a POST request is executed, the name of the class it will execute is ‘ZCL_TEST_GOS_ATTACHMENT_POST’.

METHOD GET_REST.

***************************************************************************
" VARIABLES
***************************************************************************
DATA: LV_CLASS_NAME            TYPE SEOCLSNAME.
DATA: LV_REQUEST_METHOD        TYPE STRING.
DATA: LV_ERROR                 TYPE TEXT255.

***************************************************************************
" GET REQUEST METHOD TO GET CLASS NAME TO EXECUTE
***************************************************************************
LV_REQUEST_METHOD = IO_SERVER->REQUEST->GET_HEADER_FIELD( '~request_method' ).
CONCATENATE 'ZCL_TEST_GOS_ATTACHMENT_' LV_REQUEST_METHOD INTO LV_CLASS_NAME.

***************************************************************************
" RETURN CLASS OBJECT
***************************************************************************
TRY.
CREATE OBJECT EO_REST
TYPE (LV_CLASS_NAME)
EXPORTING
IO_REQUEST   = IO_SERVER->REQUEST
IO_RESPONSE  = IO_SERVER->RESPONSE.

***************************************************************************
" CATCH ERRORS
***************************************************************************
CATCH CX_SY_CREATE_OBJECT_ERROR.
LV_ERROR = 'Method ''&'' not supported'(001).
ENDTRY.

ENDMETHOD.

1.3.    Creating the class for a POST Request

Go to ‘SE24’ and create a new class ‘ZCL_TEST_GOS_ATTACHMENT_POST’ and add interface ‘ZIF_REST’.

 

Your class will now have the first two methods below. Add a new method: ‘CONSTRUCTOR’.

 

Add the following parameters to ‘CONSTRUCTOR’. This will instantiate the class when called upon.

 

Open method ‘HANDLE_REQUEST’. This method will be executed from the handler class (‘ZCL_TEST_GOS_ATTACHMENT’).

  METHOD ZIF_REST~HANDLE_REQUEST.
**************************************************************************************
    " TYPES
**************************************************************************************
    TYPES: BEGIN OF TYPE_FILE,
             FNAME   TYPE STRING,
             FTYPE   TYPE STRING,
             QMNUM   TYPE QMNUM,
             HEXCONT TYPE XSTRING,
           END OF TYPE_FILE.

**************************************************************************************
    " VARIABLES AND OBJECTS
**************************************************************************************
    DATA: LT_RETURN             TYPE TABLE OF BAPIRET2.
    DATA: LT_HEXCONTENT         TYPE TABLE OF SOLIX.

    DATA: LS_FILE               TYPE TYPE_FILE.
    DATA: LS_NOTIF              TYPE BORIDENT.
    DATA: LS_ATTACH             TYPE BORIDENT.
    DATA: LS_DOCDATA            TYPE SODOCCHGI1.
    DATA: LS_DOCINFO            TYPE SOFOLENTI1.

    DATA: LV_DOCTYPE            TYPE SOODK-OBJTP.
    DATA: LV_FOLDER             TYPE SOOBJINFI1-OBJECT_ID.
    DATA: LV_LENGTH             TYPE I.
    DATA: LV_JSON_BODY          TYPE STRING.
    DATA: LV_NOTIF_NUMBER       TYPE QMNUM.

    DATA: LR_DESERIALIZER  TYPE REF TO CL_TREX_JSON_DESERIALIZER.

**************************************************************************************
    " INSTANTIATE VARIABLES
**************************************************************************************
    CREATE OBJECT LR_DESERIALIZER.
    LV_FOLDER = 'FOL40000000000004'.

**************************************************************************************
    " GET JSON DATA
**************************************************************************************
    LV_JSON_BODY = ME->ZIF_REST~REQUEST->GET_CDATA( ).

**************************************************************************************
    " CONVERT JSON DATA TO ABAP DATA
**************************************************************************************
    LR_JSON_DESERIALIZER->DESERIALIZE(
     EXPORTING
       JSON = LV_JSON_BODY
     IMPORTING
       ABAP = LS_FILE ).

**************************************************************************************
    " CONVERT HEX STRING TO BINARY TABLE
**************************************************************************************
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        BUFFER        = LS_FILE-HEXCONT
      IMPORTING
        OUTPUT_LENGTH = LV_LENGTH
      TABLES
        BINARY_TAB    = LT_HEXCONTENT.

**************************************************************************************
    " SET VALUES
**************************************************************************************
    LS_DOCDATA-OBJ_DESCR = LS_FILE-FNAME.
    LS_DOCDATA-OBJ_LANGU = 'E'.
    LS_DOCDATA-OBJ_NAME  = 'MESSAGE'.
    LS_DOCDATA-DOC_SIZE = XSTRLEN( LS_FILE-HEXCONT ).

    LV_DOCTYPE = LS_FILE-FTYPE.

    UNPACK LS_FILE-QMNUM TO LV_NOTIF_NUMBER.

**************************************************************************************
    " CREATE ATTACHMENT
**************************************************************************************
    CALL FUNCTION 'SO_DOCUMENT_INSERT_API1'
      EXPORTING
        FOLDER_ID     = LV_FOLDER
        DOCUMENT_DATA = LS_DOCDATA
        DOCUMENT_TYPE = LV_DOCTYPE
      IMPORTING
        DOCUMENT_INFO = LS_DOCINFO
      TABLES
        CONTENTS_HEX  = LT_HEXCONTENT.

**************************************************************************************
    " CREATE LINK TO OBJECT (Notification)
**************************************************************************************
    LS_NOTIF-OBJKEY   = LV_NOTIF_NUMBER.
    LS_NOTIF-OBJTYPE  = 'BUS2038'.

    LS_ATTACH-OBJKEY  = LS_DOCINFO-DOC_ID.
    LS_ATTACH-OBJTYPE = 'MESSAGE'.

    CALL FUNCTION 'BINARY_RELATION_CREATE'
      EXPORTING
        OBJ_ROLEA    = LS_NOTIF
        OBJ_ROLEB    = LS_ATTACH
        RELATIONTYPE = 'ATTA'.

**************************************************************************************
    " COMMIT
**************************************************************************************
    COMMIT WORK.

  ENDMETHOD.

 

For the sake of keeping it simple, I won’t be added code to method ‘SET_RESPONSE’. If you want more information on that, please read my other blog: https://blogs.sap.com/2018/06/28/writing-a-sicf-service/

 

Now open method ‘CONSTRUCTOR’ and add following code. This method will instantiate the request and response when the class is called.

METHOD CONSTRUCTOR.

ME->ZIF_REST~RESPONSE = IO_RESPONSE.
ME->ZIF_REST~REQUEST = IO_REQUEST.

ENDMETHOD.

 

1.4.    Creating a node in transaction SICF

Go to transaction ‘SICF’ and find a fitting node to which we can append a new one. In this case, we’ll opt for the already existing ‘ZREST’ node. Right click the node and add a new sub-element. We’ll name this node ‘GOS_ATTACH‘.

 

Add a fitting description for the service node.

 

Navigate to the ‘Hander List’ tab and add the handler we created (‘ZCL_TEST_GOS_ATTACHMENT’).

 

For the sake of security, navigate to the tab ‘Logon Data‘. I’m just going to fill it like this, but you should add your own security to make it safe!!!

 

 

Make sure you save the service properly.

 

Go back to the node list and right click the node you created and click ‘Activate service’.

 

Click one of the two following buttons to active the node/service.

 

2.    Creating the SAPUI5 frontend

2.1.    Creating a new project

 

Create a new project with a view and controller called ‘Main‘ and add the code below.

 

Main.view.xml

This view contains:

  • an input fields (sap.m.Input)
    • Notification number (QMNUM)
  • a file uploader (sap.ui.unified.FileUploader)
    • triggers the ‘onChange‘ event which will save the last selected file to a controller variable
  • a button (sap.m.Button)
    • triggers the ‘onConfirm‘ event which will upload the file to the SAP backend
<mvc:View controllerName="FileUploader.controller.Main" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m" xmlns:f="sap.ui.layout.form" xmlns:u="sap.ui.unified">
	<App>
		<pages>
			<Page title="File uploader">
				<content>
				<VBox class="sapUiSmallMargin">
						<Input type="text" id="idNotifNumber" placeholder="notification number" width="225px" />	
						<u:FileUploader
							id="fileUploader"
							name="myFileUpload"
							width="340px"
							uploadUrl="upload/"
							tooltip="Upload your file to the local server"
							change="onChange"/>
						<Button
							text="Upload File"
							press="onConfirm"/>
					</VBox>
				</content>
			</Page>
		</pages>
	</App>
</mvc:View>

 

Main.controller.js

This controller contains 6 methods:

  • onChange
    • passes the last selected file to a controller variable
  • onConfirm
    • is triggered when the upload button is pressed
    • executes the ‘uploadFile‘ method with the last selected file as a parameter
  • uploadFile
    • gets the data from the input fields
    • reads the file content as transforms it to hexadecimal string with the help of method ‘convertBinaryToHex
    • creates a stringied JSON object that is readable by the SAP backend with method ‘createJsonObjectForFileInfo
    • executes the ‘postAttachment‘ method
  • postAttachment
    • executes a POST request to the SAP backend
  • createJsonObjectForFileInfo
    • creates a stringied JSON object that is readable by the SAP backend
  • convertBinaryToHex
    • converts binary string to hexadecimal string
sap.ui.define([
    "sap/ui/core/mvc/Controller"
], function(Controller) {
    "use strict";

    return Controller.extend("FileUploader.controller.Main", {

        onChange: function(oEvent) { // When the user selects a file
            this.lastSelectedFile = oEvent.getParameter("files")[0];
        },

        onConfirm: function() { // When the user presses the upload button
            this.uploadFile(this.lastSelectedFile);
        },

        uploadFile: function(file) {
            var that = this;

            var fileName = file.name.split(".")[0];
            var fileType = file.name.split(".")[1];
            var notificationNumber = this.byId("idNotifNumber").getValue();
          
            var reader = new FileReader();

            reader.onload = function(e) {
                var raw = e.target.result;
                var hexString = that.convertBinaryToHex(raw).toUpperCase();
                var fileAsJsonString = that.createJsonObjectForFileInfo(fileName, fileType, notificationNumber, hexString);
                that.postAttachment(fileAsJsonString);
            };

            reader.onerror = function() {
                sap.m.MessageToast.show("error");
            };
            reader.readAsArrayBuffer(file);
        },

        postAttachment: function(fileAsJsonString) {
            var url = "http://xx.xxx.xx.xxx:8000/zrest/gos_attach?sap-client=400";
            var promise = new Promise(
                function(resolve, reject) {

                    var xhttp = new XMLHttpRequest();
                    xhttp.open("POST", url, true);

                    xhttp.onreadystatechange = function() {
                        if (this.readyState === 4) {
                            if (this.status === 200) {
                                sap.m.MessageToast.show("success");
                            } else {
                                sap.m.MessageToast.show("error");
                            }
                        }
                    }

                    xhttp.send(fileAsJsonString);
                });

            return promise;
        },
        
        // create sap json string 
        createJsonObjectForFileInfo: function(fileName, fileType, notificationNumber, hexString) {
            return '{fname:"' + fileName + '",' +
                'ftype:"' + fileType + '",' +
                'qmnum:"' + notificationNumber + '",' +
                'hexcont:"' + hexString + '"}';
        },

        convertBinaryToHex: function(buffer) {
            return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
        }
    });
});

 

 

3.    Testing the service with SAPUI5 frontend

  1. Add a notification number
  2. Select a file
  3. Press the upload button

 

That’s about it for this guide! I await your feedback. 🙂

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Nabheet Madan

    Very nice Matthijs. I remember writibg a blog to add GOS attachment to SAPGUI custom tcodes. I have a small question you created a custom web service where as it could have been done via Odata service using gateway, is it because you dont have a gateway system available or something else?

    Thanks

    Nabheey

    (0) 
  2. Matthijs Mennens Post author

    Hi Nabheet, thank you for your comment. The reason I did this via a custom web service is because it also shows another way of using web services. I figured most of the developers know how to work with a gateway. I will probably recreate this guide very soon where the gateway is used.

    (0) 
    1. Cristiano Marques

      Hi Matthijs, thanks for sharing. But when will you create a guide using sap gateway hub environment? I need to manipulate GOS through Gateway. I´m already searching in SAP Blogs,but some manuals are still confuse.

      Regards.

      (0) 

Leave a Reply