Skip to Content

Dear all,

 

Complimenting my posts on a SDK commentary component and my precious WebSocket implementation I would like to show you how you can finally share thoughts on your dashboards – live! The component is generic, uses the SAPUI5 library, can be tied to any part of the application, which can be triggered through a Design Studio script and makes use of state-of-the-art push technology for an actual real “live” experience.

 

What you get

One of the first important considerations when implementing a DesignStudio SDK extension, especially when using SAPUI5 elements, is which Design Studio dashboard modes you want to support. (commons and/or m mode). I usually try to support both, ideally exposing the same API and behavior if possible. Supplying different SDK implementations of a component for the different modes is possible since version Design Studio 1.6. You can read some more about that here.

The sap.suite.ui.commons.Timeline and sap.m.List in combination with sap.m.NotificationListItem were a perfect fit for my chat application to support both modes in one SDK component.

 

             

Fig1. Commons mode implementation       Fig2. M mode implementation

 

You can manually define messages using the property Chat Messages which uses the SDK properties Array and Object to marshal user input into JSON format on the fly. For dynamic purposes you can use the API functions:

API function Purpose
addMessage Add single message
addMessageList Add a list of messages from json string. Use the SCN utils component JSON_OBJECT for ease of use
createMessage Add single message with current timestamp
deleteMessage Delete message by given id. Usually you will need method getCurrentActiveMessageId with it
deleteMessages Delete messages by given array of ids
clear Clear chat view of all messages
getCurrentActiveMessageId Get underlying id of last clicked message

 

Furthermore you can define the date pattern using LDML format as required by sap.ui.core.format.DateFormat in property Date Pattern and Date Style. For the commons mode implementation you can also activate “SAP Social” which supplies a standard input dialog and a “reply” text link. The events On Reply and On Message Close/Select provide the Design Studio Script editors to expose these user interactions.

Fig3. Social Popup example

 

The addMessageList method expects a JSON string in the following format:

 

[{

"myId": "1",

"chatId": "TEST",

"user": "Martin",

"title": "My first commons post!",

"datetime": "20160905100120",

"text": "This is looking really cool. What do you think Jose?",

"picture": "zen/mimes/TEST_TIMELINE/img/mpankraz.jpg",

"priority": "Low"

},

{…}]

The deleteMessages method expects a JSON array in the following format:

[1, 2, 3, 4568, 455]

 

For a fully blown chat feature we still need to talk about the backend integration. That is where my WebSocket component, which I already mentioned above, comes into play. Just define the URL of the socket ws://echo.websocket.org and required protocols if needed. In case you don’t have SSO setup you might need to add user credentials to the URL during development like so ws://<username>:<password>@echo.websocket.org which is of course not very secure.

In addition to that SAP offers a special implementation of a WebSocket backend connection leveraging the Push Channel Protocol (PCP) in combination with ABAP Push Channel (APC) and ABAP Message Channel (AMC). In order to be able to connect using my Design Studio WebSocket component, you need to activate the PCP property on the component and add the protocol v10.pcp.sap.com to the protocol property.

You can use the WebSocket component’s API methods open, close, setMessage, send, getLastPushMessage and getLastPushMessagePCPFields to interact with the socket.

Once your connection is setup you have access to four events:

WebSocket component event Purpose
On Push Channel Open Fires once the WebSocket is open
On Push Channel Closed Fires once the WebSocket is closed
On Push Channel Received Fires every time a message is received from the server socket
On Push Error Message Received Fires in case the server sends a WebSocket error message

 

Let’s continue with the fun part on the SAP backend.

How it works

Using transaction SAMC you can browse all of the ABAP Message Channel example implementations relevant for the chat application. My friend, colleague and ABAP guru José Munoz got me started on the tricky parts. We ended up using the example called “ABAP ONLINE COMMUNITY” and customizing it for our needs.

We ported the command approach from the example to implement the chat history loading feature. Basically you have two options: Sending the requested chat history when the socket opens or add some kind of logic into the messages (this is what I called command approach before) and reuse the open socket. We decided that we value less WebSocket connection-overhead over a little more parsing logic to derive meaning from the messages coming from the client. Check the following ABAP Message Channel coding snippet for reference.

METHOD if_apc_wsp_ext_pcp~on_message.

    DATA: lo_producer TYPE REF TO cl_amc_message_type_pcp.
    DATA: lt_message      TYPE TABLE OF t_message,
          ls_message      LIKE LINE OF lt_message,
          lv_number_range TYPE /eaca/gl_docnr,
          lv_message_id   TYPE /eaca/gl_docnr.

    lo_producer ?= cl_amc_channel_manager=>create_message_producer(
      i_application_id = 'WS_DS_CHAT'
      i_channel_id     = '/collaborate' ).

    TRY.
*       retrieve the text message
        DATA(lv_text) = i_message->get_text( ).
        DATA(lv_command) = i_message->get_field( i_name = co_custom_comand ).

        CASE lv_command.
          WHEN 'getHistory'.

            DATA(lv_chatid) = i_message->get_field( i_name = 'chat-id' ).

            DATA(lv_history) = get_history( iv_chatid = lv_chatid ).

            DATA(body)  = |{ lv_history }|.
            DATA(pcp_message) = cl_ac_message_type_pcp=>create( ).

            pcp_message->set_field( i_name  = 'history' i_value = lv_history ).
            pcp_message->set_field( i_name  = 'chat-id' i_value = lv_chatid ).

            lo_producer->send( i_message = pcp_message ).

          WHEN 'deleteEntry'.

            lv_message_id = i_message->get_field( i_name = 'msg-id' ).

            CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
              EXPORTING
                input  = lv_message_id
              IMPORTING
                output = lv_message_id.

            DELETE FROM zapc_chat WHERE myid = lv_message_id.
            IF sy-subrc = 0.
              COMMIT WORK AND WAIT.
            ENDIF.

            DATA(lv_currchatid) = i_message->get_field( i_name = 'chat-id' ).

*           Get history in JSON format
            DATA(lv_newhistory) = get_history( iv_chatid = lv_currchatid ).

            DATA(newbody)  = |{ lv_newhistory }|.
            DATA(new_pcp_message) = cl_ac_message_type_pcp=>create( ).

*           Append JSON history as PCP field
            new_pcp_message->set_field( i_name  = 'history'
                                    i_value = lv_newhistory ).
            new_pcp_message->set_field( i_name  = 'chat-id'
                                    i_value = lv_currchatid ).

*           Send history back
            lo_producer->send( i_message = new_pcp_message ).

          WHEN OTHERS.

            REPLACE ALL OCCURRENCES OF '''' IN lv_text WITH '"'.
            /ui2/cl_json=>deserialize( EXPORTING json = |[{ lv_text }]|
                                                 pretty_name = abap_true
                                       CHANGING data = lt_message ).
            READ TABLE lt_message INTO ls_message INDEX 1.
            IF sy-subrc = 0.
              “Create your unique message identifier here (e.g number range)
              GET TIME STAMP FIELD ls_message-datetime.

              pcp_message = create_pcp_message( i_number_range = lv_number_range is_message = ls_message ).

*             Send message to channel
              lo_producer->send( i_message = pcp_message ).

*             Save message to history
              save_message( is_message = ls_message iv_number  = lv_number_range ).
            ENDIF.
        ENDCASE.

      CATCH ….

    ENDTRY.
  ENDMETHOD.

Fig. 4 PushChannel ABAP example

We use the pcp message fields and the message body to distribute and save the JSON messages coming from the Design Studio SDK chat component.

The command getHistory enables the WebSocket client to ask for a chat history by a given identifier. In my video example at the beginning BEx query names were used as identifier for instance.

The command deleteEntry enables the client to delete values from out chat history table (not part of the sequence diagram).

WSPUSHER is the instance of the Design Studio SDK WebSocket implementation and UI5CHAT the instance of the SDK Chat component. They resemble the technical names on Design Studio. For the ABAP part I chose the technical name of the class implementing the AMC and the PCP interface.

 

Fig5. Chat life cycle description

Final Words

You got introduced to an easy to use Chat SDK component which enhances your Design Studio collaboration possibilities. You can now discuss entire dashboards, individual query data breakdowns, charts or whatever Design Studio component you like. You only need to be able to apply a Design Studio script to call the WebSocket and the SDK Chat component to assign the current message thread. Make sure the message thread changes on user navigation to achieve a good chatting experience.

You can get the component by installing the latest ConVista SDK repository package.

 

As usual the code is published open-source on GitHub:

http://martinpankraz.github.io/ConVista-DS-SDK-Visualizations/

For WebSocket component install SCN SDK repository:

https://github.com/org-scn-design-studio-community/sdkpackage/

 

Feel free to leave comments and ask lots of follow up questions.

 

Yours

Martin

To report this post you need to login first.

1 Comment

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

Leave a Reply