Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[ABAP] Validate JSON data

While searching on how to validate a given JSON string, I found two options. The first simply returns a Boolean value, the second also returns information about what could be wrong.

DATA(lv_json) = '{'
             &&      '"employee": {'
             &&           |"name" : "Max", |
             &&           |"age"  : 43,    |
             &&      '}'
             && '}'.


" option 1:
DATA(is_valid) = /ui5/cl_json_util=>is_wellformed( lv_json ).

" option 2: 
DATA(lo_reader) = cl_sxml_string_reader=>create( cl_abap_codepage=>convert_to( lv_json ) ).
TRY.
    lo_reader->next_node( ).
    lo_reader->skip_node( ).
  CATCH cx_sxml_parse_error INTO DATA(lx_parse_error).
    WRITE lx_parse_error->get_text( ).
ENDTRY.

[ABAP] Generate a QR-Code

There are many blogs describing how to create a QR-Code in the context of SAPscript or Smartforms (e.g. here, here, here and here). But I was looking for a way to generate a QR-Code and only receive the graphical data stream from it, without the need for any manual steps such a creation via SE73. During my search, I found these two notes, which contained all the information I needed:

  • 2790500: mentions the class CL_RSTX_BARCODE_RENDERER with method QR_CODE
  • 2030263: describes the parameters for a QC-Code creation

Following my little test report:

PARAMETERS p_text TYPE string DEFAULT 'My QR-Code Content'.

DATA: lv_action   TYPE i.
DATA: lv_filename TYPE string.
DATA: lv_fullpath TYPE string.
DATA: lv_path     TYPE string.

TRY.
    cl_rstx_barcode_renderer=>qr_code( EXPORTING i_module_size      = 25                " Size of smallest module (in pixel, max: 32000)
                                                 i_barcode_text     = p_text            " Barcode text
*                                                 i_mode             = 'A'               " Mode ('N', 'A', 'L', 'B', 'K', 'U', '1', '2'; note 2030263)
*                                                 i_error_correction = 'H'               " Error correction ('L', 'M', 'Q', 'H')
*                                                 i_rotation         = 0                 " Rotation (0, 90, 180 or 270)
                                       IMPORTING e_bitmap           = DATA(e_bitmap) ). " Bitmap in BMP format

    DATA(lt_raw_data) = cl_bcs_convert=>xstring_to_solix( e_bitmap ).

    " Save-Dialog
    cl_gui_frontend_services=>file_save_dialog( EXPORTING default_file_name = 'QR-Code'
                                                          default_extension = 'bmp'
                                                          file_filter       = |{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING  filename          = lv_filename
                                                          path              = lv_path
                                                          fullpath          = lv_fullpath
                                                          user_action       = lv_action ).

    IF lv_action EQ cl_gui_frontend_services=>action_ok.

      " Download file to disk
      cl_gui_frontend_services=>gui_download( EXPORTING filename     = lv_fullpath
                                                        filetype     = 'BIN'
                                                        bin_filesize = xstrlen( e_bitmap )
                                              CHANGING  data_tab     = lt_raw_data ).
    ENDIF.

  CATCH cx_rstx_barcode_renderer INTO DATA(lo_exp).
    MESSAGE lo_exp->get_text( ) TYPE 'I'.
ENDTRY.

[SAPUI5] Promisify an oData request

This is discussed for many years and unfortunately will not be implemented in the UI5 framework itself (see here). There are already different blogs describing how to build a wrapper for oData requests (for example here and here).

But with ES2024 it now got super simple to do this:

async function readData(model, entitySet) {
  const [promise, resolve, reject] = Promise.withResolver( )
  model.read(entitySet, {
    success: data => resolve(data),
    error: error => reject(error)
  })
  return promise
}

const user = await readData(oDataModel, "/user")

[SAPUI5] Model binding events

this.getView().bindElement({
				path: sObjectPath,
				events: {
					dataRequested: (oEvent) => {}, // Executed when a request to server is send
					dataReceived: (oEvent) => {},  // Executed when data from server is received
					change:(oEvent) => {},         // Executed everytime you do ElementBinding
				}
			})

The events for dataRequested and dataReceived are only fired, when data is requested or data is received from a backend. This is not the case, when the requested data is already available in the model from a previous backend call. In such situations, the change event comes in handy.

The same can also be done via XML:

binding="{
  path: '/myEntitySet',
  events: {
    dataRequested: 'onDataRequested',
    dataReceived: 'onDataReceived',
    change: 'onDataChange'
  }
}"

[BTP] Get access token for specific tenant in a multitenant scenario using http rest client

https://docs.cloudfoundry.org/api/uaa/version/4.6.0/index.html#password-grant

# url from XSUAA Service Key, but replace in the url the provider subdomain with the consumer subdomain (the tenant you want to call)
@xsuaaUrl = {{$dotenv xsuaaUrl}}
# clientid from XSUAA Service Key
@xsuaaClientId = {{$dotenv xsuaaClientId}}
# clientsecret from XSUAA Service Key
@xsuaaClientSecret = {{$dotenv xsuaaClientSecret}}

@username = {{$dotenv btp_username}}
@password = {{$dotenv btp_password}}

### Get Access Token for Cloud Foundry using Password Grant with BTP default IdP
# @name getXsuaaToken
POST {{xsuaaUrl}}/oauth/token
Accept: application/json
Authorization: Basic {{xsuaaClientId}}:{{xsuaaClientSecret}}
Content-Type: application/x-www-form-urlencoded

grant_type=password
&username={{username}}
&password={{password}}
&response_type=token

### Store access token 
@access_token = {{getXsuaaToken.response.body.$.access_token}}

[BTP] How to use the refresh_token to get a new valid access_token

https://oauth.net/2/refresh-tokens

https://www.oauth.com/oauth2-servers/making-authenticated-requests/refreshing-an-access-token

https://docs.cloudfoundry.org/api/uaa/version/4.6.0/index.html#refresh-token

# url from XSUAA Service Key
@xsuaaUrl = {{$dotenv xsuaaUrl}}
# clientid from XSUAA Service Key
@xsuaaClientId = {{$dotenv xsuaaClientId}}
# clientsecret from XSUAA Service Key
@xsuaaClientSecret = {{$dotenv xsuaaClientSecret}}

#==================================================================#

### Get Access Token for Cloud Foundry using Grant Type Password with BTP default IdP 
# @name token_response
POST {{xsuaaUrl}}/oauth/token
Authorization: Basic {{xsuaaClientId}}:{{xsuaaClientSecret}}
Accept: application/json;charset=utf8
Content-Type: application/x-www-form-urlencoded

grant_type=password
&username={{$dotenv btp_username}}
&password={{$dotenv btp_password}}
&response_type=token

### Store access token and refresh token
@access_token = {{token_response.response.body.$.access_token}}
@refresh_token = {{token_response.response.body.$.refresh_token}}


### Use Refresh Token
# @name token_response
POST {{xsuaaUrl}}/oauth/token
Authorization: Basic {{xsuaaClientId}}:{{xsuaaClientSecret}}
Accept: application/json;charset=utf8
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token={{refresh_token}}

### Store access token and refresh token
@access_token = {{token_response.response.body.$.access_token}}
@refresh_token = {{token_response.response.body.$.refresh_token}}

[SAPUI5] securityTokenAvailable

Just noticed, that with UI5 version 1.119.0 the getSecurityToken() function got replaced with securityTokenAvailable().

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/getSecurityToken

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/securityTokenAvailable

// Returns the current security token if available; triggers a request to fetch the security token if it is not available.
const token = this.getModel().getSecurityToken() // Deprecated

// Returns a promise, which will resolve with the security token as soon as it is available.
const token = await this.getModel().securityTokenAvailable()

[ABAP] Progress indicator

SELECT * FROM sflight INTO TABLE @DATA(flights).

LOOP AT flights INTO DATA(flight).
  WAIT UP TO 1 SECONDS.
  cl_progress_indicator=>progress_indicate( i_text               = |Processing flight { flight-connid } as { sy-tabix } / { lines( flights ) }|
                                            i_processed          = sy-tabix
                                            i_total              = lines( flights )
                                            i_output_immediately = abap_true ).
ENDLOOP.