.
+
+ replace all occurrences of lv_match in e_out with lv_ucchar.
+
+ enddo.
+
+ endmethod.
+ENDCLASS.
diff --git a/src/zcl_ibmx_service.clas.xml b/src/zcl_ibmx_service.clas.xml
new file mode 100644
index 0000000..0e7aa4a
--- /dev/null
+++ b/src/zcl_ibmx_service.clas.xml
@@ -0,0 +1,204 @@
+
+
+
+
+
+ ZCL_IBMX_SERVICE
+ E
+ ABAP SDK for IBM Watson - Service Class
+ 1
+ X
+ X
+ X
+
+
+
+ ZCL_IBMX_SERVICE
+ ABAP_TO_JSON
+ E
+ Extracts a JSON string from an ABAP structure.
+
+
+ ZCL_IBMX_SERVICE
+ ADD_HEADER_PARAMETER
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ ADD_QUERY_PARAMETER
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ ADJUST_REQUEST_PROP
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ CHECK_HTTP_RESPONSE
+ E
+ Throws an exception, if HTTP response indicates an error.
+
+
+ ZCL_IBMX_SERVICE
+ CONSTRUCTOR
+ E
+ Class constructor.
+
+
+ ZCL_IBMX_SERVICE
+ DATETIME
+ E
+ DateTime type, format 2018-10-23T15:18:18.914xxxZ
+
+
+ ZCL_IBMX_SERVICE
+ GET_ACCESS_TOKEN
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ GET_APPNAME
+ E
+ Returns the service name.
+
+
+ ZCL_IBMX_SERVICE
+ GET_COMPONENTS
+ E
+ Returns component names of a given structure.
+
+
+ ZCL_IBMX_SERVICE
+ GET_DATATYPE
+ E
+ Returns data type of a given parameter.
+
+
+ ZCL_IBMX_SERVICE
+ GET_FIELD_TYPE
+ E
+ Returns field type and length of a given parameter.
+
+
+ ZCL_IBMX_SERVICE
+ GET_FILE_EXTENSION
+ E
+ Returns the file extension for a mime type
+
+
+ ZCL_IBMX_SERVICE
+ GET_FULL_URL
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ GET_REQUEST_PROP
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ GET_REST_CLIENT
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_DELETE
+ E
+ Sends a HTTP DELETE request.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_GET
+ E
+ Sends a HTTP GET request.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_POST
+ E
+ Sends a HTTP POST request.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_PATCH
+ E
+ Sends a HTTP PATCH request.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_POST_MULTIPART
+ E
+ Sends a HTTP POST request with multipart body.
+
+
+ ZCL_IBMX_SERVICE
+ HTTP_PUT
+ E
+ Sends a HTTP PUT request.
+
+
+ ZCL_IBMX_SERVICE
+ MERGE_STRUCTURE
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ MOVE_DATA_REFERENCE_TO_ABAP
+ E
+ Moves a referenced data structure to an ABAP structure.
+
+
+ ZCL_IBMX_SERVICE
+ NORMALIZE_URL
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ PARSE_JSON
+ E
+ Parses a JSON string into an ABAP structure.
+
+
+ ZCL_IBMX_SERVICE
+ RAISE_EXCEPTION
+ E
+ Raises an exception.
+
+
+ ZCL_IBMX_SERVICE
+ SET_ACCESS_TOKEN
+ E
+ Sets an access token explicitly.
+
+
+ ZCL_IBMX_SERVICE
+ SET_DEFAULT_QUERY_PARAMETERS
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ SET_URI_AND_AUTHORIZATION
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE
+ UNESCAPE_UNICODE
+ E
+ Unescape unicode codepoints to characters in a string
+
+
+
+
+
\ No newline at end of file
diff --git a/src/zcl_ibmx_service_arch.clas.abap b/src/zcl_ibmx_service_arch.clas.abap
new file mode 100644
index 0000000..a064680
--- /dev/null
+++ b/src/zcl_ibmx_service_arch.clas.abap
@@ -0,0 +1,566 @@
+* Copyright 2019, 2024 IBM Corp. All Rights Reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+CLASS ZCL_IBMX_SERVICE_ARCH DEFINITION
+ PUBLIC
+ CREATE PUBLIC .
+
+ PUBLIC SECTION.
+
+ INTERFACES ZIF_IBMX_SERVICE_ARCH .
+
+ TYPES to_http_client TYPE REF TO if_web_http_client .
+ TYPES to_rest_request TYPE REF TO if_web_http_request .
+ TYPES to_rest_response TYPE REF TO if_web_http_response .
+ TYPES to_form_part TYPE REF TO if_web_http_request .
+ TYPES:
+ BEGIN OF ts_client,
+ http TYPE to_http_client,
+ request TYPE to_rest_request,
+ END OF ts_client .
+ TYPES:
+ BEGIN OF ts_http_status,
+ code TYPE n LENGTH 3,
+ reason TYPE string,
+ json TYPE string,
+ END OF ts_http_status .
+ TYPES ts_header TYPE ZIF_IBMX_SERVICE_ARCH~ts_header .
+ TYPES tt_header TYPE ZIF_IBMX_SERVICE_ARCH~tt_header .
+ TYPES ts_url TYPE ZIF_IBMX_SERVICE_ARCH~ts_url .
+ TYPES ts_access_token TYPE ZIF_IBMX_SERVICE_ARCH~ts_access_token .
+ TYPES ts_request_prop TYPE ZIF_IBMX_SERVICE_ARCH~ts_request_prop .
+
+ "! Returns the user's time zone.
+ "!
+ "! @parameter E_TIMEZONE | user's time zone
+ "!
+ CLASS-METHODS get_timezone
+ RETURNING
+ VALUE(e_timezone) TYPE ZIF_IBMX_SERVICE_ARCH~ty_timezone .
+ "! Returns an ABAP module identifier.
+ "!
+ "! @parameter E_PROGNAME | ABAP module identifier
+ "!
+ CLASS-METHODS get_progname
+ RETURNING
+ VALUE(e_progname) TYPE string .
+ "! Decodes base64 encoded data to binary.
+ "!
+ "! @parameter I_BASE64 | Base64-encoded binary
+ "! @parameter E_BINARY | Binary data
+ "! @raising ZCX_IBMX_SERVICE_EXCEPTION | Exception being raised in case of an error.
+ "!
+ CLASS-METHODS base64_decode
+ IMPORTING
+ !i_base64 TYPE string
+ RETURNING
+ VALUE(e_binary) TYPE xstring
+ RAISING
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Returns a HTTP/REST client based on an URL.
+ "!
+ "! @parameter I_URL | URL
+ "! @parameter I_REQUEST_PROP | Request parameters
+ "! @parameter E_CLIENT | HTTP/REST client
+ "! @raising ZCX_IBMX_SERVICE_EXCEPTION | Exception being raised in case of an error.
+ "!
+ CLASS-METHODS create_client_by_url
+ IMPORTING
+ !i_url TYPE string
+ !i_request_prop TYPE ts_request_prop
+ EXPORTING
+ !e_client TYPE ts_client
+ RAISING
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Returns the default proxy host and port.
+ "!
+ "! @parameter I_URL | target URL
+ "! @parameter E_PROXY_HOST | Proxy host
+ "! @parameter E_PROXY_PORT | Proxy port
+ "!
+ CLASS-METHODS get_default_proxy
+ IMPORTING
+ !i_url TYPE ts_url OPTIONAL
+ EXPORTING
+ !e_proxy_host TYPE string
+ !e_proxy_port TYPE string .
+ "! Sets request header for basic authentication.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_USERNAME | User name
+ "! @parameter I_PASSWORD | Password
+ "!
+ CLASS-METHODS set_authentication_basic
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_username TYPE string
+ !i_password TYPE string .
+ "! Sets a HTTP header.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_NAME | Header field name
+ "! @parameter I_VALUE | Header field value
+ "!
+ CLASS-METHODS set_request_header
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_name TYPE string
+ !i_value TYPE string .
+ "! Sets the URI for a HTTP request.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_URI | URI
+ "!
+ CLASS-METHODS set_request_uri
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_uri TYPE string .
+ "! Generates a multi-part request body.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter IT_FORM_PART | Table of form parts
+ "! @raising ZCX_IBMX_SERVICE_EXCEPTION | Exception being raised in case of an error.
+ "!
+ METHODS add_form_part
+ IMPORTING
+ !i_client TYPE ts_client
+ !it_form_part TYPE ZIF_IBMX_SERVICE_ARCH~tt_form_part
+ RAISING
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Executes a HTTP request.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_METHOD | HTTP method (GET,POST,PUT,DELETE)
+ "! @parameter E_RESPONSE | Response of the request
+ "! @raising ZCX_IBMX_SERVICE_EXCEPTION | Exception being raised in case of an error.
+ "!
+ CLASS-METHODS execute
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_method TYPE ZIF_IBMX_SERVICE_ARCH~char
+ RETURNING
+ VALUE(e_response) TYPE to_rest_response
+ RAISING
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Reads character data from a HTTP response.
+ "!
+ "! @parameter I_RESPONSE | HTTP response
+ "! @parameter E_DATA | Character data
+ "!
+ CLASS-METHODS get_response_string
+ IMPORTING
+ !i_response TYPE REF TO if_web_http_response
+ RETURNING
+ VALUE(e_data) TYPE string .
+ "! Set character data for the body of a HTTP request.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_DATA | Character data
+ "!
+ CLASS-METHODS set_request_body_cdata
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_data TYPE string .
+ "! Set binary data for the body of a HTTP request.
+ "!
+ "! @parameter I_CLIENT | HTTP/REST client
+ "! @parameter I_DATA | Binary data
+ "!
+ CLASS-METHODS set_request_body_xdata
+ IMPORTING
+ !i_client TYPE ts_client
+ !i_data TYPE xstring .
+ "! Reads binary data from a HTTP response.
+ "!
+ "! @parameter I_RESPONSE | HTTP response
+ "! @parameter E_DATA | Binary data
+ "!
+ CLASS-METHODS get_response_binary
+ IMPORTING
+ !i_response TYPE REF TO if_web_http_response
+ RETURNING
+ VALUE(e_data) TYPE xstring .
+ "! Returns a HTTP response header.
+ "!
+ "! @parameter I_RESPONSE | HTTP/REST response
+ "! @parameter I_HEADER_FIELD | Header field name
+ "! @parameter E_VALUE | Header field value
+ "!
+ CLASS-METHODS get_response_header
+ IMPORTING
+ !i_response TYPE to_rest_response
+ !i_header_field TYPE string
+ RETURNING
+ VALUE(e_value) TYPE string .
+ "! Returns the status of a REST response.
+ "!
+ "! @parameter I_REST_RESPONSE | HTTP/REST response
+ "! @parameter E_STATUS | HTTP status
+ "!
+ CLASS-METHODS get_http_status
+ IMPORTING
+ !i_rest_response TYPE REF TO if_web_http_response
+ RETURNING
+ VALUE(e_status) TYPE ts_http_status .
+ "! Converts STRING data to UTF8 encoded XSTRING.
+ "!
+ "! @parameter I_STRING | STRING data
+ "! @parameter E_UTF8 | UTF8-encoded data
+ "!
+ CLASS-METHODS convert_string_to_utf8
+ IMPORTING
+ !i_string TYPE string
+ RETURNING
+ VALUE(e_utf8) TYPE xstring
+ RAISING
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Finds (and replaces) a regular expression.
+ "!
+ "! @parameter I_REGEX | Regular expression
+ "! @parameter I_WITH | Replacement (if omitted, FIND is performed)
+ "! @parameter I_ALL_OCCURRENCES | 'X' if ALL OCCURRENCES OF should be used
+ "! @parameter I_IGNORING_CASE | 'X', if IGNORING CASE should be used
+ "! @parameter I_IN | String to be searched
+ "! @parameter E_OFFSET | Returns position of occurrence
+ "! @parameter C_SUBMATCH1 | 1st submatch
+ "! @parameter C_SUBMATCH2 | 2nd submatch
+ "! @parameter C_SUBMATCH3 | 3rd submatch
+ "! @parameter C_IN | String to be searched and returned
+ "! @parameter E_SUBRC | sy-subrc of FIND / REPLACE
+ "!
+ CLASS-METHODS find_regex
+ IMPORTING
+ !i_regex TYPE string
+ !i_with TYPE string OPTIONAL
+ !i_all_occurrences TYPE ZIF_IBMX_SERVICE_ARCH=>boolean DEFAULT 'X'
+ !i_ignoring_case TYPE ZIF_IBMX_SERVICE_ARCH=>boolean OPTIONAL
+ !i_in TYPE string OPTIONAL
+ EXPORTING
+ !e_offset TYPE int4
+ CHANGING
+ !c_submatch1 TYPE string OPTIONAL
+ !c_submatch2 TYPE string OPTIONAL
+ !c_submatch3 TYPE string OPTIONAL
+ !c_in TYPE string OPTIONAL
+ RETURNING
+ VALUE(e_subrc) TYPE sysubrc .
+ PROTECTED SECTION.
+ PRIVATE SECTION.
+ENDCLASS.
+
+
+
+CLASS ZCL_IBMX_SERVICE_ARCH IMPLEMENTATION.
+
+
+ METHOD base64_decode.
+
+ e_binary = cl_web_http_utility=>decode_x_base64( i_base64 ).
+
+* if sy-subrc <> 0.
+* ZCL_IBMX_SERVICE=>raise_exception( i_msgno = '030' ). " Decoding of base64 string failed
+* endif.
+
+ ENDMETHOD.
+
+
+ METHOD convert_string_to_utf8.
+
+ CALL METHOD cl_web_http_utility=>encode_utf8
+ EXPORTING
+ unencoded = i_string
+ RECEIVING
+ encoded = e_utf8
+ EXCEPTIONS
+ OTHERS = 1.
+ IF sy-subrc <> 0.
+ ZCL_IBMX_SERVICE=>raise_exception( i_text = 'Cannot convert string to UTF-8' ) ##NO_TEXT.
+ ENDIF.
+
+ ENDMETHOD.
+
+
+ METHOD create_client_by_url.
+
+ DATA:
+ lv_text TYPE string.
+
+ TRY.
+ "create http destination by url
+ DATA(lo_http_destination) =
+ cl_http_destination_provider=>create_by_url( i_url ).
+ CATCH cx_http_dest_provider_error.
+ ENDTRY.
+
+ "create HTTP client by destination
+ TRY.
+ e_client-http = cl_web_http_client_manager=>create_by_http_destination( lo_http_destination ) .
+ CATCH cx_web_http_client_error.
+ lv_text = `HTTP client cannot be created: ` && lv_text ##NO_TEXT.
+ ZCL_IBMX_SERVICE=>raise_exception( i_text = lv_text ).
+ ENDTRY.
+
+ e_client-request = e_client-http->get_http_request( ).
+
+ ENDMETHOD.
+
+
+ METHOD execute.
+
+ DATA:
+ lo_request TYPE REF TO if_web_http_request,
+ lv_method TYPE string,
+ lv_text TYPE string,
+ lo_exception TYPE REF TO cx_web_http_client_error.
+
+ TRY.
+ CASE i_method.
+ WHEN ZIF_IBMX_SERVICE_ARCH~c_method_get.
+ lv_method = 'GET'.
+ e_response = i_client-http->execute( if_web_http_client=>get ).
+ WHEN ZIF_IBMX_SERVICE_ARCH~c_method_post.
+ lv_method = 'POST'.
+ e_response = i_client-http->execute( if_web_http_client=>post ).
+ WHEN ZIF_IBMX_SERVICE_ARCH~c_method_put.
+ lv_method = 'PUT'.
+ e_response = i_client-http->execute( if_web_http_client=>put ).
+ WHEN ZIF_IBMX_SERVICE_ARCH~c_method_patch.
+ lv_method = 'PATCH'.
+ e_response = i_client-http->execute( if_web_http_client=>patch ).
+ WHEN ZIF_IBMX_SERVICE_ARCH~c_method_delete.
+ lv_method = 'DELETE'.
+ e_response = i_client-http->execute( if_web_http_client=>delete ).
+ WHEN others.
+ lv_method = ZIF_IBMX_SERVICE_ARCH~c_not_supported.
+ ENDCASE.
+
+ CATCH cx_web_http_client_error INTO lo_exception.
+ lv_text = lo_exception->get_text( ).
+ lv_text = `HTTP ` && lv_method && ` request failed: ` && lv_text ##NO_TEXT.
+ ZCL_IBMX_SERVICE=>raise_exception( i_text = lv_text i_previous = lo_exception ).
+ ENDTRY.
+
+ IF lv_method EQ ZIF_IBMX_SERVICE_ARCH~c_not_supported.
+ ZCL_IBMX_SERVICE=>raise_exception( i_text = ZIF_IBMX_SERVICE_ARCH~c_not_supported ).
+ ENDIF.
+
+ ENDMETHOD.
+
+
+ METHOD add_form_part.
+
+ DATA:
+ ls_form_part TYPE ZIF_IBMX_SERVICE_ARCH~ts_form_part,
+ lo_part TYPE REF TO if_web_http_request.
+
+ LOOP AT it_form_part INTO ls_form_part.
+
+ lo_part = i_client-request->add_multipart( ).
+
+ IF NOT ls_form_part-content_type IS INITIAL.
+ lo_part->set_header_field( i_name = `Content-Type` i_value = ls_form_part-content_type ) ##NO_TEXT.
+ ENDIF.
+
+ IF NOT ls_form_part-content_disposition IS INITIAL.
+ lo_part->set_header_field( i_name = `Content-Disposition` i_value = ls_form_part-content_disposition ) ##NO_TEXT.
+ ELSE.
+ lo_part->set_header_field( i_name = `Content-Disposition` i_value = `form-data; name="unnamed"` ) ##NO_TEXT.
+ ENDIF.
+
+ IF NOT ls_form_part-xdata IS INITIAL.
+ DATA(lv_length) = xstrlen( ls_form_part-xdata ).
+ lo_part->set_binary( i_data = ls_form_part-xdata i_offset = 0 i_length = lv_length ).
+ ENDIF.
+
+ IF NOT ls_form_part-cdata IS INITIAL.
+ lo_part->set_text( i_text = ls_form_part-cdata ).
+ ENDIF.
+
+ ENDLOOP.
+
+ ENDMETHOD.
+
+
+ METHOD get_default_proxy.
+
+ ENDMETHOD.
+
+
+ METHOD get_http_status.
+ DATA: ls_status TYPE if_web_http_response=>http_status.
+
+ ls_status = i_rest_response->get_status( ).
+ e_status-code = ls_status-code.
+ e_status-reason = ls_status-reason.
+ TRY.
+ e_status-json = i_rest_response->get_text( ).
+ CATCH cx_web_message_error.
+ " response may be binary -> ignore
+ e_status-json = ''.
+ ENDTRY.
+ ENDMETHOD.
+
+
+ METHOD get_progname.
+
+* e_progname = sy-cprog.
+
+ ENDMETHOD.
+
+
+ METHOD get_response_binary.
+
+ e_data = i_response->get_binary( ).
+
+ ENDMETHOD.
+
+
+ METHOD get_response_header.
+
+ e_value = i_response->get_header_field( i_name = i_header_field ).
+
+ ENDMETHOD.
+
+
+ METHOD get_response_string.
+
+ e_data = i_response->get_text( ).
+
+ ENDMETHOD.
+
+
+ METHOD get_timezone.
+
+* e_timezone = sy-zonlo.
+
+ ENDMETHOD.
+
+
+ METHOD set_authentication_basic.
+
+ i_client-request->set_authorization_basic(
+ EXPORTING
+ i_username = i_username
+ i_password = i_password
+ ).
+
+ ENDMETHOD.
+
+
+ METHOD set_request_body_cdata.
+
+ i_client-request->set_text( i_text = i_data ).
+
+ ENDMETHOD.
+
+
+ METHOD set_request_body_xdata.
+
+ i_client-request->set_binary( i_data = i_data ).
+
+ ENDMETHOD.
+
+
+ METHOD set_request_header.
+
+ i_client-request->set_header_field( i_name = i_name i_value = i_value ) .
+
+ ENDMETHOD.
+
+
+ METHOD set_request_uri.
+
+ DATA:
+ lo_request TYPE REF TO if_web_http_request.
+
+ i_client-request->set_uri_path( i_uri_path = i_uri ).
+
+ ENDMETHOD.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMC_SERVICE_ARCH=>FIND_REGEX
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_REGEX TYPE STRING
+* | [--->] I_WITH TYPE STRING(optional)
+* | [--->] I_ALL_OCCURRENCES TYPE BOOLEAN (default ='X')
+* | [--->] I_IGNORING_CASE TYPE BOOLEAN(optional)
+* | [--->] I_IN TYPE STRING(optional)
+* | [<---] E_OFFSET TYPE INT4
+* | [<-->] C_SUBMATCH1 TYPE STRING(optional)
+* | [<-->] C_SUBMATCH2 TYPE STRING(optional)
+* | [<-->] C_SUBMATCH3 TYPE STRING(optional)
+* | [<-->] C_IN TYPE STRING
+* | [<-()] E_SUBRC TYPE SYSUBRC
+* +--------------------------------------------------------------------------------------
+ METHOD find_regex.
+
+ DATA:
+ l_in TYPE string.
+
+ IF NOT i_with IS SUPPLIED.
+ " FIND
+ IF i_in IS SUPPLIED.
+ l_in = i_in.
+ ELSE.
+ l_in = c_in.
+ ENDIF.
+ IF c_submatch3 IS SUPPLIED.
+ IF i_ignoring_case EQ 'X'.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset IGNORING CASE SUBMATCHES c_submatch1 c_submatch2 c_submatch3.
+ ELSE.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset SUBMATCHES c_submatch1 c_submatch2 c_submatch3.
+ ENDIF.
+ ELSEIF c_submatch2 IS SUPPLIED.
+ IF i_ignoring_case EQ 'X'.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset IGNORING CASE SUBMATCHES c_submatch1 c_submatch2.
+ ELSE.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset SUBMATCHES c_submatch1 c_submatch2.
+ ENDIF.
+ ELSEIF c_submatch1 IS SUPPLIED.
+ IF i_ignoring_case EQ 'X'.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset IGNORING CASE SUBMATCHES c_submatch1.
+ ELSE.
+ FIND PCRE i_regex IN l_in MATCH OFFSET e_offset SUBMATCHES c_submatch1.
+ ENDIF.
+ ELSE.
+ IF i_ignoring_case EQ 'X'.
+ FIND PCRE i_regex IN c_in MATCH OFFSET e_offset IGNORING CASE.
+ ELSE.
+ FIND PCRE i_regex IN c_in MATCH OFFSET e_offset.
+ ENDIF.
+ ENDIF.
+
+ ELSE.
+ " REPLACE
+ e_offset = 0.
+ IF i_all_occurrences EQ 'X'.
+ IF i_ignoring_case EQ 'X'.
+ REPLACE ALL OCCURRENCES OF PCRE i_regex IN c_in WITH i_with IGNORING CASE.
+ ELSE.
+ REPLACE ALL OCCURRENCES OF PCRE i_regex IN c_in WITH i_with.
+ ENDIF.
+ ELSE.
+ IF i_ignoring_case EQ 'X'.
+ REPLACE FIRST OCCURRENCE OF PCRE i_regex IN c_in WITH i_with IGNORING CASE.
+ ELSE.
+ REPLACE FIRST OCCURRENCE OF PCRE i_regex IN c_in WITH i_with.
+ ENDIF.
+ ENDIF.
+
+ ENDIF.
+
+ e_subrc = sy-subrc.
+
+ ENDMETHOD.
+ENDCLASS.
diff --git a/src/zcl_ibmx_service_arch.clas.xml b/src/zcl_ibmx_service_arch.clas.xml
new file mode 100644
index 0000000..2abcc3c
--- /dev/null
+++ b/src/zcl_ibmx_service_arch.clas.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ E
+ IBM Watson SDK Platform-specifics
+ 1
+ X
+ X
+ X
+
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ ADD_FORM_PART
+ E
+ Generates a multi-part request body.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ BASE64_DECODE
+ E
+ Decodes base64 encoded data to binary.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ CONVERT_STRING_TO_UTF8
+ E
+ Converts STRING data to UTF8 encoded XSTRING.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ CREATE_CLIENT_BY_URL
+ E
+ Returns a HTTP/REST client based on an URL.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ EXECUTE
+ E
+ Executes a HTTP request.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ FIND_REGEX
+ E
+ Finds (and replaces) a regular expression.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_DEFAULT_PROXY
+ E
+ Returns the default proxy host and port.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_HTTP_STATUS
+ E
+ Returns the status of a REST response.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_PROGNAME
+ E
+ Returns an ABAP module identifier.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_RESPONSE_BINARY
+ E
+ Reads binary data from a HTTP response.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_RESPONSE_HEADER
+ E
+ Returns a HTTP response header.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_RESPONSE_STRING
+ E
+ Reads character data from a HTTP response.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_REST_REQUEST
+ E
+ Returns a request object from a HTTP client object.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ GET_TIMEZONE
+ E
+ Returns the user's time zone.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ SET_AUTHENTICATION_BASIC
+ E
+ Sets request header for basic authentication.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ SET_REQUEST_BODY_CDATA
+ E
+ Set character data for the body of a HTTP request.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ SET_REQUEST_BODY_XDATA
+ E
+ Set binary data for the body of a HTTP request.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ SET_REQUEST_HEADER
+ E
+ Sets a HTTP header.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ SET_REQUEST_URI
+ E
+ Sets the URI for a HTTP request.
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ TO_HTTP_CLIENT
+ E
+ HTTP Client Control Block
+
+
+ ZCL_IBMX_SERVICE_ARCH
+ TO_REST_CLIENT
+ E
+ REST HTTP Client
+
+
+
+
+
\ No newline at end of file
diff --git a/src/zcl_ibmx_service_ext.clas.abap b/src/zcl_ibmx_service_ext.clas.abap
new file mode 100644
index 0000000..0d929fa
--- /dev/null
+++ b/src/zcl_ibmx_service_ext.clas.abap
@@ -0,0 +1,1182 @@
+* Copyright 2019, 2024 IBM Corp. All Rights Reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+class ZCL_IBMX_SERVICE_EXT definition
+ public
+ inheriting from ZCL_IBMX_SERVICE
+ create public .
+
+public section.
+
+ types:
+ ty_instance_id(32) type c .
+ types:
+ ty_servicename(30) type c .
+ types:
+ ty_image_format(3) type c .
+ types TY_IMAGE_CLASS type STRING .
+ types:
+ begin of ts_oauth_prop,
+ url type ts_url,
+ username type string,
+ password type string,
+ apikey type string,
+ end of ts_oauth_prop .
+
+ constants C_FIELD_NONE type FIELDNAME value '###' ##NO_TEXT.
+ constants C_FORMAT_JPG type TY_IMAGE_FORMAT value 'jpg' ##NO_TEXT.
+ constants C_FORMAT_PNG type TY_IMAGE_FORMAT value 'png' ##NO_TEXT.
+ constants C_FORMAT_GIF type TY_IMAGE_FORMAT value 'gif' ##NO_TEXT.
+ constants C_FORMAT_TIF type TY_IMAGE_FORMAT value 'tif' ##NO_TEXT.
+ constants C_FORMAT_ZIP type TY_IMAGE_FORMAT value 'zip' ##NO_TEXT.
+ constants C_FORMAT_ALL type TY_IMAGE_FORMAT value '*' ##NO_TEXT.
+ constants C_FORMAT_UNKNOWN type TY_IMAGE_FORMAT value '###' ##NO_TEXT.
+ constants C_TOKEN_GENERATION_NEVER type CHAR value 'N' ##NO_TEXT.
+ constants C_TOKEN_GENERATION_ALWAYS type CHAR value 'A' ##NO_TEXT.
+ constants C_TOKEN_GENERATION_AUTO type CHAR value SPACE ##NO_TEXT.
+ constants C_IAM_TOKEN_HOST type STRING value 'iam.cloud.ibm.com' ##NO_TEXT.
+ constants C_IAM_TOKEN_PATH type STRING value '/identity/token' ##NO_TEXT.
+ constants C_WATSONX_TOKEN_PATH type STRING value '/icp4d-api/v1/authorize' ##NO_TEXT.
+ constants C_ICP4D_TOKEN_PATH type STRING value '/v1/preauth/validateAuth' ##NO_TEXT.
+ data P_INSTANCE_ID type TY_INSTANCE_ID .
+ data P_SERVICENAME type TY_SERVICENAME .
+
+ "! Returns the bearer token, if available.
+ "!
+ "! @parameter E_BEARER_TOKEN | Access token.
+ "!
+ methods GET_BEARER_TOKEN
+ returning
+ value(E_BEARER_TOKEN) type STRING .
+ "! Returns the SDK built date.
+ "!
+ "! @parameter E_SDK_VERSION_DATE | Built data in format YYYYMMDD.
+ "!
+ methods GET_SDK_VERSION_DATE
+ returning
+ value(E_SDK_VERSION_DATE) type STRING .
+ "! Method for internal use.
+ "!
+ methods SET_BEARER_TOKEN
+ importing
+ !I_BEARER_TOKEN type STRING .
+ "! Retrieves a value of a configuration parameter
from table ZIBMX_CONFIG.
+ "!
+ "! @parameter I_DEFAULT | Default value, if configuration parameter is not found.
+ "! @parameter I_PARAM | Configuration parameter name.
+ "!
+ methods GET_CONFIG_VALUE
+ importing
+ !I_DEFAULT type ZIBMX_CONFIG-VALUE optional
+ !I_PARAM type ZIBMX_CONFIG-PARAM
+ returning
+ value(E_VALUE) type ZIBMX_CONFIG-VALUE
+ raising
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Factory method to instantiate a service wrapper class.
+ "!
+ "! @parameter I_INSTANCE_ID |
+ "! Value of field INSTANCE_UID in table ZIBMX_CONFIG.
+ "! Service credentials are read from table ZIBMX_CONFIG with key SERVICE = Name of service wrapper class without prefix ZCL_IBMX_ and INSTANCE_UID.
+ "! @parameter I_URL | URL of the service.
+ "! @parameter I_HOST | Host of the service. I_URL and I_HOST can be used synonymously.
+ "! @parameter I_USERNAME | User password to authenticate on service.
+ "! @parameter I_PASSWORD | User password to authenticate on service.
+ "! @parameter I_PROXY_HOST | Proxy server, not applicable on SAP Cloud Platform.
+ "! @parameter I_PROXY_PORT | Proxy server port, not applicable on SAP Cloud Platform.
+ "! @parameter I_APIKEY | API key password to authenticate on service.
+ "! @parameter I_AUTH_METHOD | Authentication method. Possible values are "IAM", "ICP4D", "basicAuth", "NONE".
+ "! @parameter I_OAUTH_PROP | Credentials to generate token for OAuth authentication.
+ "! @parameter I_TOKEN_GENERATION | Method for access token refresh:
+ "! C_TOKEN_GENERATION_NEVER - Access token is not refreshed.
+ "! C_TOKEN_GENERATION_ALWAYS - Access to token is refreshed for each service call.
+ "! C_TOKEN_GENERATION_AUTO - Access to token is refreshed just before it expires.
+ "! @parameter I_REQUEST_HEADERS | List of headers sent with every request, format 'Header1=Value1;Header2=Value2'.
+ "! @parameter I_VERSION | Value of query parameter VERSION.
+ "!
+ class-methods GET_INSTANCE
+ importing
+ !I_INSTANCE_ID type TY_INSTANCE_ID optional
+ !I_URL type STRING optional
+ !I_HOST type STRING optional
+ !I_USERNAME type STRING optional
+ !I_PASSWORD type STRING optional
+ !I_PROXY_HOST type STRING optional
+ !I_PROXY_PORT type STRING optional
+ !I_APIKEY type STRING optional
+ !I_AUTH_METHOD type STRING default C_DEFAULT
+ !I_OAUTH_PROP type TS_OAUTH_PROP optional
+ !I_ACCESS_TOKEN type TS_ACCESS_TOKEN optional
+ !I_TOKEN_GENERATION type CHAR default C_TOKEN_GENERATION_AUTO
+ !I_REQUEST_HEADERS type STRING optional
+ !I_VERSION type STRING optional
+ exporting
+ value(EO_INSTANCE) type ANY .
+ "! Compresses multiple images from an internal table
to a one or more xstrings in ZIP format.
+ "!
+ "! @parameter IT_EXAMPLES | Internal table of images.
+ "! @parameter IV_FIELD_CLASS |
+ "! Field in IT_EXAMPLES that contains the file class.
+ "! All records in IT_EXAMPLES with same class will be compress into one ZIP xstring.
+ "! @parameter IV_FIELD_FILENAME | Field in IT_EXAMPLES that contains the filename for the image.
+ "! @parameter IV_FIELD_IMAGE | Field in IT_EXAMPLES that contains the image.
+ "! @parameter IV_IMAGE_FORMAT | Format of the image. Possible values are
+ "! C_FORMAT_JPG, C_FORMAT_PNG, C_FORMAT_GIF, C_FORMAT_TIF, C_FORMAT_ALL.
+ "! @parameter IV_IMAGE_NAME | Name of the image.
+ "! @parameter ET_ZIPDATA | Internal table containing a compressed ZIP xstring for each image class.
+ "! @raising ZCX_IBMX_SERVICE_EXCEPTION | Exception being raised in case of an error.
+ "!
+ class-methods GET_ZIPDATA
+ importing
+ !IT_EXAMPLES type ANY TABLE
+ !IV_FIELD_CLASS type FIELDNAME default C_FIELD_NONE
+ !IV_FIELD_FILENAME type FIELDNAME optional
+ !IV_FIELD_IMAGE type FIELDNAME optional
+ !IV_IMAGE_FORMAT type TY_IMAGE_FORMAT optional
+ !IV_IMAGE_NAME type STRING optional
+ exporting
+ !ET_ZIPDATA type TT_MAP_FILE
+ raising
+ ZCX_IBMX_SERVICE_EXCEPTION .
+
+ methods GET_ACCESS_TOKEN
+ redefinition .
+ methods GET_REQUEST_PROP
+ redefinition .
+protected section.
+private section.
+
+ data P_OAUTH_PROP type TS_OAUTH_PROP .
+ data P_TOKEN_GENERATION type CHAR .
+
+ "! Method for internal use.
+ "!
+ class-methods ADD_CONFIG_PROP
+ importing
+ !I_SERVICENAME type TY_SERVICENAME
+ !I_INSTANCE_ID type TY_INSTANCE_ID optional
+ changing
+ !C_REQUEST_PROP type ANY .
+ "! Method for internal use.
+ "!
+ class-methods ADD_IMAGE_TO_ZIP
+ importing
+ !IS_TABLELINE type ANY
+ !IO_ZIP type ref to CL_ABAP_ZIP
+ !IV_BASE64 type BOOLEAN
+ !IV_FILENAME type STRING optional
+ !IV_FIELD_IMAGE type FIELDNAME
+ !IV_FIELD_FILENAME type FIELDNAME optional
+ raising
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ "! Method for internal use.
+ "!
+ class-methods GET_FIELD_DATA
+ importing
+ !IS_TABLELINE type ANY
+ !IV_FIELD_CLASS type FIELDNAME optional
+ !IV_FIELD_FILENAME type FIELDNAME optional
+ !IV_FIELD_IMAGE type FIELDNAME optional
+ exporting
+ !EV_FIELD_CLASS type FIELDNAME
+ !EV_FIELD_FILENAME type FIELDNAME
+ !EV_FIELD_IMAGE type FIELDNAME
+ !EV_FIELD_IMAGE_BASE64 type FIELDNAME
+ raising
+ ZCX_IBMX_SERVICE_EXCEPTION .
+ENDCLASS.
+
+
+
+CLASS ZCL_IBMX_SERVICE_EXT IMPLEMENTATION.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Private Method ZCL_IBMX_SERVICE_EXT=>ADD_CONFIG_PROP
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_SERVICENAME TYPE TY_SERVICENAME
+* | [--->] I_INSTANCE_ID TYPE TY_INSTANCE_ID(optional)
+* | [<-->] C_REQUEST_PROP TYPE ANY
+* +--------------------------------------------------------------------------------------
+ method add_config_prop.
+
+ data:
+ lt_compname type tt_string,
+ lv_type type char,
+ ls_config type ZIBMX_config,
+ lt_config type standard table of ZIBMX_config,
+ lt_ref type standard table of ref to data,
+ lr_data type ref to data,
+ lv_index type i value 0,
+ ls_url type ts_url value is initial.
+
+ field-symbols:
+ type any,
+ type any,
+ type string,
+ type any.
+
+ " read config table
+ if not i_instance_id is initial.
+ select *
+ from ZIBMX_config
+ where service = @i_servicename and
+ instance_uid = @i_instance_id
+ into table @lt_config.
+ else.
+ select *
+ from ZIBMX_config
+ where service = @i_servicename and
+ instance_uid = @space
+ into table @lt_config.
+ endif.
+
+ " quit, if no relevant entry in config table exists
+ check sy-subrc = 0.
+
+ " change config parameter "URL" to "HOST"
+ ls_config-param = 'HOST'.
+ modify lt_config from ls_config transporting param where param = 'URL'.
+
+ " perform breadth-first search on c_request_prop to allow setting values on deeper levels,
+ " e.g. HOST -> c_request_prop-url-host
+ lr_data = ref #( c_request_prop ).
+ append lr_data to lt_ref.
+
+ do.
+ lv_index = lv_index + 1.
+ read table lt_ref into lr_data index lv_index.
+ if sy-subrc <> 0.
+ exit.
+ endif.
+ assign lr_data->* to .
+
+ get_components(
+ exporting
+ i_structure =
+ importing
+ e_components = lt_compname ).
+
+ loop at lt_compname assigning .
+ assign component of structure to .
+ check sy-subrc = 0.
+ get_field_type(
+ exporting
+ i_field =
+ importing
+ e_technical_type = lv_type ).
+ if lv_type eq ZIF_IBMX_service_arch~c_datatype-struct or
+ lv_type eq ZIF_IBMX_service_arch~c_datatype-struct_deep.
+ lr_data = ref #( ).
+ append lr_data to lt_ref.
+ else.
+ if is initial.
+ read table lt_config into ls_config with key param = ##WARN_OK.
+ if sy-subrc = 0.
+ condense ls_config-value.
+ = ls_config-value.
+ endif.
+ endif.
+ endif.
+ endloop.
+
+ enddo.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Private Method ZCL_IBMX_SERVICE_EXT=>ADD_IMAGE_TO_ZIP
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] IS_TABLELINE TYPE ANY
+* | [--->] IO_ZIP TYPE REF TO CL_ABAP_ZIP
+* | [--->] IV_BASE64 TYPE BOOLEAN
+* | [--->] IV_FILENAME TYPE STRING(optional)
+* | [--->] IV_FIELD_IMAGE TYPE FIELDNAME
+* | [--->] IV_FIELD_FILENAME TYPE FIELDNAME(optional)
+* | [!CX!] ZCX_IBMX_SERVICE_EXCEPTION
+* +--------------------------------------------------------------------------------------
+ method add_image_to_zip.
+ data:
+ lx_image type xstring,
+ lv_filename type string.
+ field-symbols:
+ type any,
+ type any.
+
+ if not iv_filename is initial.
+ lv_filename = iv_filename.
+ else.
+ assign component iv_field_filename of structure is_tableline to .
+ lv_filename = conv #( ). " type conversion
+ if lv_filename is initial.
+ raise_exception( i_msgno = '056' ).
+ endif.
+ endif.
+
+ assign component iv_field_image of structure is_tableline to .
+
+ if iv_base64 eq c_boolean_true.
+
+ lx_image = base64_decode( i_base64 = ).
+
+ io_zip->add(
+ name = lv_filename
+ content = lx_image ).
+
+ else.
+
+ io_zip->add(
+ name = lv_filename
+ content = ).
+
+ endif.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->GET_ACCESS_TOKEN
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_REQUEST_PROP TYPE TS_REQUEST_PROP(optional)
+* | [<-()] E_ACCESS_TOKEN TYPE TS_ACCESS_TOKEN
+* | [!CX!] ZCX_IBMX_SERVICE_EXCEPTION
+* +--------------------------------------------------------------------------------------
+ method get_access_token.
+ data:
+ lo_response type to_rest_response,
+ lv_grand_urlencoded type string,
+ lv_key_urlencoded type string,
+ lv_json type string,
+ lv_seconds type i,
+ lv_timestamp type timestamp,
+ ls_token type ZIBMX_token value is initial,
+ ls_timestamp type timestamp,
+ begin of ls_token_classic,
+ token type string,
+ end of ls_token_classic.
+
+ if not i_request_prop-access_token-access_token is initial.
+ ls_token-access_token = p_request_prop_default-access_token-access_token.
+ ls_token-token_type = p_request_prop_default-access_token-token_type.
+ ls_token-expires_ts = p_request_prop_default-access_token-expires_ts.
+ else.
+
+ select single *
+ from ZIBMX_token
+ where service = @p_servicename and
+ instance_uid = @p_instance_id
+ into @ls_token.
+
+ endif.
+
+ " check if access token has expired
+ get time stamp field ls_timestamp.
+ if ls_timestamp >= ls_token-expires_ts.
+ clear ls_token.
+ endif.
+
+ " (re)new token unless it is still valid
+ if ls_token-access_token is initial and p_token_generation ne c_token_generation_never.
+
+ data:
+ ls_token_request_prop type ts_request_prop.
+
+ ls_token_request_prop-url = p_oauth_prop-url.
+ " set to tribool_false to distinguish between false and inital
+ ls_token_request_prop-auth_basic = c_tribool_false.
+ ls_token_request_prop-auth_oauth = c_tribool_false.
+ ls_token_request_prop-auth_apikey = c_tribool_false.
+ ls_token_request_prop-header_accept = ZCL_IBMX_service=>ZIF_IBMX_service_arch~c_mediatype-appl_json.
+
+
+ if i_request_prop-auth_name eq 'IAM' or i_request_prop-auth_name eq 'BearerToken' ##NO_TEXT.
+
+ " write urlencoded parameters
+ if not ls_token-refresh_token is initial.
+ lv_grand_urlencoded = escape( val = 'refresh_token' format = cl_abap_format=>e_uri_full ) ##NO_TEXT.
+ lv_key_urlencoded = escape( val = ls_token-refresh_token format = cl_abap_format=>e_uri_full ).
+ ls_token_request_prop-body = `grant_type=` && lv_grand_urlencoded && `&refresh_token=` && lv_key_urlencoded ##NO_TEXT.
+ else.
+ if not p_oauth_prop-apikey is initial.
+ lv_grand_urlencoded = escape( val = 'urn:ibm:params:oauth:grant-type:apikey' format = cl_abap_format=>e_uri_full ) ##NO_TEXT.
+ lv_key_urlencoded = escape( val = p_oauth_prop-apikey format = cl_abap_format=>e_uri_full ).
+ ls_token_request_prop-body = `grant_type=` && lv_grand_urlencoded && `&apikey=` && lv_key_urlencoded ##NO_TEXT.
+ elseif not p_oauth_prop-password is initial.
+ lv_grand_urlencoded = escape( val = 'urn:ibm:params:oauth:grant-type:password' format = cl_abap_format=>e_uri_full ) ##NO_TEXT.
+ lv_key_urlencoded = escape( val = p_oauth_prop-password format = cl_abap_format=>e_uri_full ).
+ ls_token_request_prop-body = `grant_type=` && lv_grand_urlencoded && `&username=` && p_oauth_prop-username && `&password=` && p_oauth_prop-password ##NO_TEXT.
+ else.
+ ls_token_request_prop-username = p_oauth_prop-username.
+ ls_token_request_prop-password = p_oauth_prop-password.
+ ls_token_request_prop-apikey = p_oauth_prop-apikey.
+ endif.
+ endif.
+ ls_token_request_prop-header_content_type = ZCL_IBMX_service=>ZIF_IBMX_service_arch~c_mediatype-appl_www_form_urlencoded.
+
+ " execute HTTP POST request
+ lo_response = http_post( i_request_prop = ls_token_request_prop ).
+
+ " receive response json
+ lv_json = get_response_string( lo_response ).
+
+ " call json parser (ignore properties that do not exist in abap structure)
+ parse_json(
+ exporting
+ i_json = lv_json
+ changing
+ c_abap = ls_token ).
+
+ else.
+
+ if i_request_prop-auth_body eq c_boolean_true.
+
+ " POST request having username/password in body (e.g. for Db2 Warehouse)
+ ls_token_request_prop-body = `{ "userid": "` && p_oauth_prop-username && `", "password": "` && p_oauth_prop-password && `" }` ##NO_TEXT.
+ ls_token_request_prop-header_content_type = ZCL_IBMX_service=>ZIF_IBMX_service_arch~c_mediatype-appl_json.
+
+ " execute HTTP POST request
+ lo_response = http_post( i_request_prop = ls_token_request_prop ).
+
+ else.
+
+ ls_token_request_prop-username = p_oauth_prop-username.
+ ls_token_request_prop-password = p_oauth_prop-password.
+ ls_token_request_prop-apikey = p_oauth_prop-apikey.
+ ls_token_request_prop-url = p_oauth_prop-url.
+ ls_token_request_prop-auth_basic = c_boolean_true.
+
+ " execute HTTP GET request
+ lo_response = http_get( i_request_prop = ls_token_request_prop ).
+
+ " receive response json
+ lv_json = get_response_string( lo_response ).
+
+ if i_request_prop-auth_name eq 'ICP4D' ##NO_TEXT.
+
+ " parse expected response:
+ " { "username": "joe", "role": "User", "uid": "1003",
+ " "accessToken": "eyJhbGc...1AjT_w",
+ " "messageCode": "success", "message": "success" }
+ data:
+ begin of ls_access_token_icp4d,
+ accesstoken type string,
+ end of ls_access_token_icp4d.
+ parse_json(
+ exporting
+ i_json = lv_json
+ changing
+ c_abap = ls_access_token_icp4d ).
+
+ ls_token-access_token = ls_access_token_icp4d-accesstoken.
+ ls_token-expires_in = 12 * 3600.
+
+ else.
+
+ " call json parser
+ parse_json(
+ exporting
+ i_json = lv_json
+ changing
+ c_abap = ls_token_classic ).
+
+ ls_token-access_token = ls_token_classic-token.
+ ls_token-expires_in = 3600.
+
+ endif.
+
+ ls_token-token_type = 'Bearer' ##NO_TEXT.
+
+ endif.
+
+ endif.
+
+
+ " calculate expiration time
+ if ls_token-expires_in > 0.
+ get time stamp field lv_timestamp.
+ lv_seconds = ls_token-expires_in - 300. " subtract 5 minutes to be save
+ ls_token-expires_ts = cl_abap_tstmp=>add( tstmp = lv_timestamp secs = lv_seconds ) ##TYPE.
+ endif.
+
+ ls_token-service = p_servicename.
+ ls_token-instance_uid = p_instance_id.
+
+ if p_token_generation eq c_token_generation_auto.
+ modify ZIBMX_token from @ls_token.
+ commit work.
+ endif.
+
+ endif.
+
+ " fill returning parameter
+ move-corresponding ls_token to e_access_token ##ENH_OK.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->GET_BEARER_TOKEN
+* +-------------------------------------------------------------------------------------------------+
+* | [<-()] E_BEARER_TOKEN TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method get_bearer_token.
+
+ data:
+ ls_access_token type ts_access_token.
+
+ try.
+ ls_access_token = get_access_token( ).
+ catch ZCX_IBMX_service_exception.
+ clear ls_access_token.
+ endtry.
+
+
+ if ls_access_token-token_type eq 'Bearer'.
+ e_bearer_token = ls_access_token-access_token.
+ else.
+ clear e_bearer_token.
+ endif.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->GET_CONFIG_VALUE
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_DEFAULT TYPE ZIBMX_CONFIG-VALUE(optional)
+* | [--->] I_PARAM TYPE ZIBMX_CONFIG-PARAM
+* | [<-()] E_VALUE TYPE ZIBMX_CONFIG-VALUE
+* | [!CX!] ZCX_IBMX_SERVICE_EXCEPTION
+* +--------------------------------------------------------------------------------------
+ method get_config_value.
+
+
+ select single value
+ from ZIBMX_config
+ where param = @i_param and
+ service = @p_servicename and
+ instance_uid = @p_instance_id
+ into @e_value.
+
+ if sy-subrc <> 0.
+ select single value
+ from ZIBMX_config
+ where param = @i_param and
+ service = @p_servicename and
+ instance_uid = @space
+ into @e_value.
+
+ if sy-subrc <> 0.
+ if i_default is supplied.
+ e_value = i_default.
+ else.
+ raise exception type ZCX_IBMX_service_exception.
+ endif.
+ endif.
+
+ endif.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Private Method ZCL_IBMX_SERVICE_EXT=>GET_FIELD_DATA
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] IS_TABLELINE TYPE ANY
+* | [--->] IV_FIELD_CLASS TYPE FIELDNAME(optional)
+* | [--->] IV_FIELD_FILENAME TYPE FIELDNAME(optional)
+* | [--->] IV_FIELD_IMAGE TYPE FIELDNAME(optional)
+* | [<---] EV_FIELD_CLASS TYPE FIELDNAME
+* | [<---] EV_FIELD_FILENAME TYPE FIELDNAME
+* | [<---] EV_FIELD_IMAGE TYPE FIELDNAME
+* | [<---] EV_FIELD_IMAGE_BASE64 TYPE FIELDNAME
+* | [!CX!] ZCX_IBMX_SERVICE_EXCEPTION
+* +--------------------------------------------------------------------------------------
+ method get_field_data.
+
+ data:
+ begin of ls_comp,
+ name type string,
+ type type char,
+ length type i,
+ end of ls_comp,
+ lt_comp like standard table of ls_comp,
+ lt_compname type tt_string,
+ lv_msg1 type string.
+ field-symbols:
+ type any.
+
+ get_components(
+ exporting
+ i_structure = is_tableline
+ importing
+ e_components = lt_compname ).
+
+ loop at lt_compname into ls_comp-name.
+ assign component ls_comp-name of structure is_tableline to .
+ get_field_type(
+ exporting
+ i_field =
+ importing
+ e_technical_type = ls_comp-type
+ e_length = ls_comp-length ).
+ append ls_comp to lt_comp.
+ endloop.
+
+ clear: ev_field_class, ev_field_filename, ev_field_image, ev_field_image_base64.
+
+ if ev_field_class is requested.
+ if not iv_field_class is initial.
+ " field that contains class name is explicit specified
+ ev_field_class = iv_field_class.
+ else.
+ " field CLASS
+ read table lt_comp with key name = 'CLASS' into ls_comp.
+ if sy-subrc = 0 and
+ ( ls_comp-type eq 'C' or ls_comp-type eq 'g' or ls_comp-type eq 'N' ). " character type
+ ev_field_class = ls_comp-name.
+ else.
+ " first field of character type and length 3 or more contains class name
+ loop at lt_comp into ls_comp where ( type eq 'C' or type eq 'N' ) and length >= 3 and
+ name ne 'FILENAME' and name ne 'IMAGE' and name ne 'IMAGE_BASE64'.
+ ev_field_class = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ if ev_field_class is initial.
+ " first field of type string contains class name
+ loop at lt_comp into ls_comp where type eq 'g' and
+ name ne 'FILENAME' and name ne 'IMAGE' and name ne 'IMAGE_BASE64'.
+ ev_field_class = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ endif.
+ endif.
+
+
+ if ev_field_filename is requested.
+ if not iv_field_filename is initial.
+ " field that contains image name is explicit specified
+ ev_field_filename = iv_field_filename.
+ else.
+ read table lt_comp with key name = 'FILENAME' into ls_comp.
+ if sy-subrc = 0 and
+ ( ls_comp-type eq 'C' or ls_comp-type eq 'g' or ls_comp-type eq 'N' ).
+ ev_field_filename = ls_comp-name.
+ else.
+ " first field of character type and length 6 or more contains image name
+ loop at lt_comp into ls_comp where ( type eq 'C' or type eq 'N' ) and length >= 6 and
+ name ne ev_field_class and name ne 'IMAGE' and name ne 'IMAGE_BASE64'.
+ ev_field_filename = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ if ev_field_filename is initial.
+ " first field of type string contains image name
+ loop at lt_comp into ls_comp where type eq 'g' and name ne ev_field_class.
+ ev_field_filename = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ endif.
+ endif.
+
+
+ if ev_field_image is requested.
+ if not iv_field_image is initial.
+ " field that contains image data is explicit specified
+
+ " check image field data type to determine encoding
+ read table lt_comp into ls_comp with key name = iv_field_image.
+ if sy-subrc <> 0.
+ lv_msg1 = conv #( ev_field_image ). " type conversion
+ raise_exception(
+ i_msgno = '054' " Field &1 is missing or has incompatible type.
+ i_msgv1 = lv_msg1 ).
+ endif.
+ if ls_comp-type eq 'g'.
+ " image is base64 encoded
+ ev_field_image_base64 = iv_field_image.
+ else.
+ " image in binary data
+ ev_field_image = iv_field_image.
+ endif.
+
+ else.
+ read table lt_comp with key name = 'IMAGE' into ls_comp.
+ if sy-subrc <> 0.
+ read table lt_comp with key name = 'IMAGE_BASE64' into ls_comp.
+ endif.
+ if sy-subrc = 0 and
+ ( ls_comp-type eq 'y' or ls_comp-type eq 'g' ). " xstring or string
+ if ls_comp-type eq 'g'.
+ ev_field_image_base64 = ls_comp-name.
+ else.
+ ev_field_image = ls_comp-name.
+ endif.
+ else.
+ " first field of type xstring contains image data (binary)
+ loop at lt_comp into ls_comp where type eq 'y' and
+ name ne ev_field_class and name ne ev_field_filename.
+ ev_field_image = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ if ev_field_image is initial.
+ " first field of type string contains image data (base64 encoded)
+ loop at lt_comp into ls_comp where type eq 'g' and
+ name ne ev_field_class and name ne ev_field_filename.
+ ev_field_image_base64 = ls_comp-name.
+ exit.
+ endloop.
+ endif.
+ endif.
+ endif.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_SERVICE_EXT=>GET_INSTANCE
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_INSTANCE_ID TYPE TY_INSTANCE_ID(optional)
+* | [--->] I_URL TYPE STRING(optional)
+* | [--->] I_HOST TYPE STRING(optional)
+* | [--->] I_USERNAME TYPE STRING(optional)
+* | [--->] I_PASSWORD TYPE STRING(optional)
+* | [--->] I_PROXY_HOST TYPE STRING(optional)
+* | [--->] I_PROXY_PORT TYPE STRING(optional)
+* | [--->] I_APIKEY TYPE STRING(optional)
+* | [--->] I_AUTH_METHOD TYPE STRING (default =C_DEFAULT)
+* | [--->] I_OAUTH_PROP TYPE TS_OAUTH_PROP(optional)
+* | [--->] I_ACCESS_TOKEN TYPE TS_ACCESS_TOKEN(optional)
+* | [--->] I_TOKEN_GENERATION TYPE CHAR (default =C_TOKEN_GENERATION_AUTO)
+* | [--->] I_REQUEST_HEADERS TYPE STRING(optional)
+* | [--->] I_VERSION TYPE STRING(optional)
+* | [<---] EO_INSTANCE TYPE ANY
+* +--------------------------------------------------------------------------------------
+ method get_instance.
+ data:
+ lv_classname type string,
+ ls_request_prop type ts_request_prop,
+ lo_instance type ref to ZCL_IBMX_service_ext,
+ lt_headerstr type tt_string,
+ lv_headerstr type string,
+ ls_header type ts_header,
+ lv_str type string.
+
+ " instantiate object of type of exporting parameter
+ get_field_type(
+ exporting
+ i_field = eo_instance
+ importing
+ e_relative_type = lv_classname ).
+
+ create object eo_instance type (lv_classname)
+ exporting
+ i_url = i_url
+ i_host = i_host
+ i_proxy_host = i_proxy_host
+ i_proxy_port = i_proxy_port
+ i_username = i_username
+ i_password = i_password
+ i_apikey = i_apikey.
+
+ lo_instance ?= eo_instance.
+
+ " Set service name (= class name without namespace and prefix 'CL_')
+ if lv_classname cp 'Z*'.
+ "find first occurrence of regex 'ZCL_[^_]*_([^\/]*)$' in lv_classname submatches lo_instance->p_servicename.
+ find_regex(
+ exporting
+ i_regex = 'ZCL_[^_]*_([^\/]*)$'
+ i_all_occurrences = space
+ i_in = lv_classname
+ changing
+ c_submatch1 = lv_str ).
+ lo_instance->p_servicename = conv #( lv_str ).
+ else.
+ "find first occurrence of regex 'CL_([^\/]*)$' in lv_classname submatches lo_instance->p_servicename.
+ find_regex(
+ exporting
+ i_regex = 'CL_([^\/]*)$'
+ i_all_occurrences = space
+ i_in = lv_classname
+ changing
+ c_submatch1 = lv_str ).
+ lo_instance->p_servicename = conv #( lv_str ).
+ endif.
+ if lo_instance->p_servicename is initial.
+ "find first occurrence of regex '([^\/]*)$' in lv_classname submatches lo_instance->p_servicename.
+ find_regex(
+ exporting
+ i_regex = '([^\/]*)$'
+ i_all_occurrences = space
+ i_in = lv_classname
+ changing
+ c_submatch1 = lv_str ).
+ lo_instance->p_servicename = conv #( lv_str ).
+ if lo_instance->p_servicename is initial.
+ lo_instance->p_servicename = lv_classname.
+ endif.
+ endif.
+
+ " Set default request headers
+ split i_request_headers at ';' into table lt_headerstr.
+ if sy-subrc = 0.
+ loop at lt_headerstr into lv_headerstr.
+ split lv_headerstr at '=' into ls_header-name ls_header-value.
+ if sy-subrc = 0.
+ append ls_header to lo_instance->p_request_prop_default-headers.
+ endif.
+ endloop.
+ endif.
+
+ " Set instance ID
+ if not i_instance_id is initial.
+ lo_instance->p_instance_id = i_instance_id.
+ else.
+ lo_instance->p_instance_id = get_progname( ).
+ endif.
+
+ " Merge properties from config table for this service and instance
+ add_config_prop(
+ exporting
+ i_servicename = lo_instance->p_servicename
+ i_instance_id = lo_instance->p_instance_id
+ changing
+ c_request_prop = lo_instance->p_request_prop_default ).
+
+ normalize_url( changing c_url = lo_instance->p_request_prop_default-url ).
+
+ " Merge properties from config table for this service type
+ add_config_prop(
+ exporting
+ i_servicename = lo_instance->p_servicename
+ changing
+ c_request_prop = lo_instance->p_request_prop_default ).
+
+ normalize_url( changing c_url = lo_instance->p_request_prop_default-url ).
+
+ " Get service default properties
+ ls_request_prop = lo_instance->get_request_prop( i_auth_method = i_auth_method ).
+ merge_structure(
+ exporting
+ i_alternative = ls_request_prop
+ changing
+ c_base = lo_instance->p_request_prop_default ).
+
+ " Ensure that OAuth is set in case IAM is used.
+ if lo_instance->p_request_prop_default-auth_name eq 'IAM'.
+ lo_instance->p_request_prop_default-auth_oauth = c_boolean_true.
+ endif.
+
+ " Set OAuth properties
+ lo_instance->p_oauth_prop = i_oauth_prop.
+ normalize_url(
+ changing
+ c_url = lo_instance->p_oauth_prop-url ).
+ if ls_request_prop-auth_name eq 'IAM' or ls_request_prop-auth_name eq 'ICP4D' or ls_request_prop-auth_name eq 'BearerToken' ##NO_TEXT.
+ if lo_instance->p_oauth_prop-url-host is initial.
+ lo_instance->p_oauth_prop-url-protocol = 'https' ##NO_TEXT.
+ if ls_request_prop-auth_name eq 'IAM' or ls_request_prop-auth_name eq 'BearerToken' ##NO_TEXT.
+ lo_instance->p_oauth_prop-url-host = c_iam_token_host.
+ else.
+ lo_instance->p_oauth_prop-url-host = lo_instance->p_request_prop_default-url-host.
+ endif.
+ endif.
+ if lo_instance->p_oauth_prop-url-path_base is initial and lo_instance->p_oauth_prop-url-path is initial.
+ " Set path_base (not path), otherwise the default service path_base would be added, which is not correct
+ if ls_request_prop-auth_name eq 'IAM' or ls_request_prop-auth_name eq 'BearerToken' ##NO_TEXT.
+ lo_instance->p_oauth_prop-url-path_base = c_iam_token_path.
+ elseif ls_request_prop-auth_name eq 'WATSONX'.
+ lo_instance->p_oauth_prop-url-path_base = c_watsonx_token_path.
+ else.
+ lo_instance->p_oauth_prop-url-path_base = c_icp4d_token_path.
+ endif.
+ endif.
+ if lo_instance->p_oauth_prop-password is initial and lo_instance->p_oauth_prop-apikey is initial.
+ lo_instance->p_oauth_prop-username = lo_instance->p_request_prop_default-username.
+ lo_instance->p_oauth_prop-password = lo_instance->p_request_prop_default-password.
+ lo_instance->p_oauth_prop-apikey = lo_instance->p_request_prop_default-apikey.
+ endif.
+ endif.
+
+ if not i_access_token-access_token is initial.
+ lo_instance->p_token_generation = c_token_generation_never.
+ lo_instance->set_access_token( i_access_token = i_access_token ).
+ else.
+ lo_instance->p_token_generation = i_token_generation.
+ endif.
+
+ lo_instance->p_version = i_version.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->GET_REQUEST_PROP
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_AUTH_METHOD TYPE STRING (default =C_DEFAULT)
+* | [<-()] E_REQUEST_PROP TYPE TS_REQUEST_PROP
+* +--------------------------------------------------------------------------------------
+ method get_request_prop.
+ data:
+ lv_auth_method type string.
+
+ e_request_prop = super->get_request_prop( i_auth_method = i_auth_method ).
+
+ lv_auth_method = i_auth_method.
+ if lv_auth_method eq c_default.
+ lv_auth_method = 'IAM'.
+ endif.
+ if lv_auth_method is initial.
+ e_request_prop-auth_basic = c_boolean_false.
+ e_request_prop-auth_oauth = c_boolean_false.
+ e_request_prop-auth_apikey = c_boolean_false.
+ elseif lv_auth_method eq 'IAM'.
+ e_request_prop-auth_name = 'IAM'.
+ e_request_prop-auth_type = 'apiKey'.
+ e_request_prop-auth_headername = 'Authorization'.
+ e_request_prop-auth_header = c_boolean_true.
+ elseif lv_auth_method eq 'ICP4D'.
+ e_request_prop-auth_name = 'ICP4D'.
+ e_request_prop-auth_type = 'apiKey'.
+ e_request_prop-auth_headername = 'Authorization'.
+ e_request_prop-auth_header = c_boolean_true.
+ elseif lv_auth_method eq 'basicAuth'.
+ e_request_prop-auth_name = 'basicAuth'.
+ e_request_prop-auth_type = 'http'.
+ e_request_prop-auth_basic = c_boolean_true.
+ endif.
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->GET_SDK_VERSION_DATE
+* +-------------------------------------------------------------------------------------------------+
+* | [<-()] E_SDK_VERSION_DATE TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method get_sdk_version_date.
+
+ e_sdk_version_date = '20240325'.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_SERVICE_EXT=>GET_ZIPDATA
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] IT_EXAMPLES TYPE ANY TABLE
+* | [--->] IV_FIELD_CLASS TYPE FIELDNAME (default =C_FIELD_NONE)
+* | [--->] IV_FIELD_FILENAME TYPE FIELDNAME(optional)
+* | [--->] IV_FIELD_IMAGE TYPE FIELDNAME(optional)
+* | [--->] IV_IMAGE_FORMAT TYPE TY_IMAGE_FORMAT(optional)
+* | [--->] IV_IMAGE_NAME TYPE STRING(optional)
+* | [<---] ET_ZIPDATA TYPE TT_MAP_FILE
+* | [!CX!] ZCX_IBMX_SERVICE_EXCEPTION
+* +--------------------------------------------------------------------------------------
+ method get_zipdata.
+
+ constants:
+ c_class_all type string value '#ALL#'.
+
+ data:
+ lv_field_class type fieldname,
+ lv_field_filename type fieldname,
+ lv_field_image type fieldname,
+ lv_field_image_base64 type fieldname,
+ lv_field_image_act type fieldname,
+ lv_base64 type boolean,
+ lr_data type ref to data,
+ ls_zipdata type ts_map_file,
+ lv_image_count type i,
+ lv_class type ty_image_class,
+ lt_classes type standard table of ty_image_class,
+ lv_filename type string,
+ lv_filename_suffix(4) type n value 0,
+ lo_zip type ref to cl_abap_zip value is initial,
+ lv_image_name type string,
+ lv_image_format type ty_image_format.
+ field-symbols:
+ type any,
+ type any,
+ type any,
+ type any.
+
+ create data lr_data like line of it_examples.
+ assign lr_data->* to .
+
+ " determine field with filename
+ if iv_field_filename is initial or iv_field_filename eq c_field_none.
+ if not iv_image_name is initial.
+ lv_field_filename = c_field_none.
+ lv_image_name = iv_image_name.
+ endif.
+ else.
+ lv_field_filename = iv_field_filename.
+ clear: lv_image_name, lv_image_format.
+ endif.
+
+ " get field names
+ get_field_data(
+ exporting
+ is_tableline =
+ iv_field_class = iv_field_class
+ iv_field_filename = lv_field_filename
+ iv_field_image = iv_field_image
+ importing
+ ev_field_class = lv_field_class
+ ev_field_filename = lv_field_filename
+ ev_field_image = lv_field_image
+ ev_field_image_base64 = lv_field_image_base64 ).
+
+ " reset field name for filename if explicitly set to 'none'.
+ if lv_field_filename eq c_field_none.
+ clear lv_field_filename.
+ endif.
+
+
+ if iv_field_class eq c_field_none.
+ " do not separate into different classes
+ append c_class_all to lt_classes.
+ else.
+ " compile list of different classes
+ loop at it_examples assigning .
+ assign component lv_field_class of structure to .
+ append to lt_classes.
+ endloop.
+ sort lt_classes.
+ delete adjacent duplicates from lt_classes.
+ endif.
+
+ " determine default image format
+ if iv_image_format is initial or
+ iv_image_format eq c_format_all or
+ iv_image_format eq c_format_unknown.
+ lv_image_format = c_format_jpg.
+ else.
+ lv_image_format = iv_image_format.
+ endif.
+ translate lv_image_format to lower case.
+
+
+ " generate a zip data xstring per class
+ loop at lt_classes into lv_class.
+ check not lv_class is initial.
+
+ create object lo_zip.
+
+ lv_image_count = 0.
+
+ loop at it_examples assigning .
+ if lv_class ne c_class_all.
+ assign component lv_field_class of structure to .
+ check eq lv_class.
+ endif.
+
+ " determine filename
+ clear lv_filename.
+ if not lv_field_filename is initial.
+ assign component lv_field_filename of structure to .
+ if sy-subrc = 0.
+ " remove path from filename
+ "find regex '([^/\\]*)$' in submatches lv_filename.
+ find_regex(
+ exporting
+ i_regex = '([^/\\]*)$'
+ i_in =
+ changing
+ c_submatch1 = lv_filename ).
+ endif.
+ endif.
+
+ " if valid filename was not found, use the default
+ if lv_filename np '*+.+++'.
+ if lv_filename is initial.
+ if lv_image_name is initial.
+ lv_filename = lv_class && lv_filename_suffix && `.` && lv_image_format.
+ else.
+ lv_filename = lv_image_name && lv_filename_suffix && `.` && lv_image_format.
+ endif.
+ else.
+ lv_filename = lv_filename && `.` && lv_image_format.
+ endif.
+ lv_filename_suffix = lv_filename_suffix + 1.
+ endif.
+
+ " determine image encoding
+ if lv_field_image is initial.
+ lv_field_image_act = lv_field_image_base64.
+ lv_base64 = c_boolean_true.
+ elseif lv_field_image_base64 is initial.
+ lv_field_image_act = lv_field_image.
+ lv_base64 = c_boolean_false.
+ else.
+ assign component lv_field_image of structure to .
+ if not is initial.
+ lv_field_image_act = lv_field_image.
+ lv_base64 = c_boolean_false.
+ else.
+ lv_field_image_act = lv_field_image_base64.
+ lv_base64 = c_boolean_true.
+ endif.
+ endif.
+
+ " add image to zip data
+ add_image_to_zip(
+ exporting
+ io_zip = lo_zip
+ is_tableline =
+ iv_filename = lv_filename
+ iv_field_image = lv_field_image_act
+ iv_base64 = lv_base64 ).
+
+ lv_image_count = lv_image_count + 1.
+
+ endloop.
+
+ if lv_image_count > 0.
+
+ ls_zipdata-key = lv_class.
+ ls_zipdata-data = lo_zip->save( ).
+ append ls_zipdata to et_zipdata.
+
+ endif.
+
+ endloop.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Instance Public Method ZCL_IBMX_SERVICE_EXT->SET_BEARER_TOKEN
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_BEARER_TOKEN TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method set_bearer_token.
+
+ data:
+ ls_access_token type ts_access_token.
+
+ ls_access_token-token_type = 'Bearer' ##NO_TEXT.
+ ls_access_token-access_token = i_bearer_token.
+ try.
+ set_access_token( i_access_token = ls_access_token ).
+ catch ZCX_IBMX_service_exception ##NO_HANDLER.
+ endtry.
+
+ endmethod.
+ENDCLASS.
diff --git a/src/zcl_ibmx_service_ext.clas.xml b/src/zcl_ibmx_service_ext.clas.xml
new file mode 100644
index 0000000..1df0f96
--- /dev/null
+++ b/src/zcl_ibmx_service_ext.clas.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+ ZCL_IBMX_SERVICE_EXT
+ E
+ Extended REST API service class
+ 1
+ X
+ X
+ X
+
+
+
+ ZCL_IBMX_SERVICE_EXT
+ ADD_CONFIG_PROP
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE_EXT
+ ADD_IMAGE_TO_ZIP
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FIELD_NONE
+ E
+ Field Name
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_ALL
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_GIF
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_JPG
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_PNG
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_TIF
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_UNKNOWN
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ C_FORMAT_ZIP
+ E
+ Image encoding (jpg, png, ...)
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_BEARER_TOKEN
+ E
+ Returns the bearer token, if available.
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_CONFIG_VALUE
+ E
+ Retrieves a value of a configuration parameter
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_FIELD_DATA
+ E
+ Method for internal use.
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_INSTANCE
+ E
+ Factory method to instantiate a service specific wrapper cla
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_SDK_VERSION_DATE
+ E
+ Returns the SDK built date.
+
+
+ ZCL_IBMX_SERVICE_EXT
+ GET_ZIPDATA
+ E
+ Compresses multiple images from an internal table
+
+
+ ZCL_IBMX_SERVICE_EXT
+ SET_BEARER_TOKEN
+ E
+ Method for internal use.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/zcl_ibmx_util.clas.abap b/src/zcl_ibmx_util.clas.abap
new file mode 100644
index 0000000..e2a96a8
--- /dev/null
+++ b/src/zcl_ibmx_util.clas.abap
@@ -0,0 +1,611 @@
+* Copyright 2019, 2024 IBM Corp. All Rights Reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+class ZCL_IBMX_util definition
+ public
+ final
+ create public .
+
+public section.
+
+
+ "! Converts an internal table to JSON string in table schema format.
+ "! E.g.: '{"fields": ["PET", "NUMBER"], "values": [["Cat",5],["Rabbit",2]]}'
+ "!
+ "! @parameter I_ITAB | Internal table to be converted.
+ "! @parameter I_DICTIONARY | Dictionary to be used for mapping ABAP identifiers to JSON keys.
+ "! @parameter I_LOWER_CASE | If set to C_BOOLEAN_TRUE all keys in JSON string will be lower case.
+ "! @parameter IT_EXCLUDED_FIELDS | Internal table of table fields in I_ITAB that should not occur in result.
+ "! @parameter E_JSON | JSON string.
+ "!
+ class-methods itab_to_tableschema
+ importing
+ !i_itab type any table
+ !i_dictionary type any optional
+ !i_lower_case type ZCL_IBMX_service=>boolean default ZCL_IBMX_service=>c_boolean_false
+ !it_excluded_fields type ZCL_IBMX_service=>tt_string optional
+ returning
+ value(e_json) type string .
+ "! Converts a JSON string in table schema format to an internal table.
+ "! E.g.: '{"tableschema_key": {"fields": ["PET", "NUMBER"], "values": [["Cat",5],["Rabbit",2]]}}'
+ "!
+ "! @parameter I_JSON | JSON string.
+ "! @parameter I_TABLESCHEMA_KEY | Key in JSON string that holds the table schema.
+ "! @parameter E_ITAB | Internal table containing converted data.
+ "!
+ class-methods tableschema_to_itab
+ importing
+ !i_json type string
+ !i_tableschema_key type string optional
+ exporting
+ !e_itab type any table .
+ "! Converts a timestamp in UTC to a timestamp in local time.
+ "!
+ "! @parameter IV_TIMESTAMP | Timestamp (UTC).
+ "! @parameter EV_LOCAL | Timestamp (local time).
+ "!
+ class-methods convert_timestamp_to_local
+ importing
+ !iv_timestamp type timestamp
+ returning
+ value(ev_local) type string .
+ "! Determine MIME type from a file name, e.g. 'docu.txt' -> 'text_/plain'.
+ "!
+ "! @parameter I_FILENAME | Filename with extension.
+ "! @parameter E_MIMETYPE | MIME type.
+ "!
+ class-methods get_mimetype_from_extension
+ importing
+ !i_filename type string
+ returning
+ value(e_mimetype) type string .
+ "! Converts a timestamp in UTC to a timestamp in another timezone.
+ "!
+ "! @parameter I_TIMESTAMP | Timestamp (UTC).
+ "! @parameter I_TIMEZONE | Time zone
+ "! @parameter E_TIMESTAMP | Timestamp in give time zone.
+ "!
+ class-methods utc_to_timezone
+ importing
+ !i_timestamp type timestamp
+ !i_timezone type ZIF_IBMX_service_arch=>ty_timezone optional
+ returning
+ value(e_timestamp) type timestamp .
+ "! Converts datetime format to a timestamp, i.e. yyyy-mm-ddThh:mm:ssZ -> YYYYMMDDHHMMSS
+ "!
+ "! @parameter I_DATETIME | Input in datetime format.
+ "! @parameter E_TIMESTAMP | Timestamp.
+ "!
+ class-methods convert_datetime_to_timestamp
+ importing
+ !i_datetime type ZCL_IBMX_service=>datetime
+ returning
+ value(e_timestamp) type timestamp .
+ "! Converts a timestamp to datetime format, i.e. YYYYMMDDHHMMSS -> yyyy-mm-ddThh:mm:ssZ
+ "!
+ "! @parameter I_TIMESTAMP | Timestamp.
+ "! @parameter E_DATETIME | Datetime format.
+ "!
+ class-methods convert_timestamp_to_datetime
+ importing
+ !i_timestamp type timestamp
+ returning
+ value(e_datetime) type ZCL_IBMX_service=>datetime .
+protected section.
+private section.
+ENDCLASS.
+
+
+
+CLASS ZCL_IBMX_UTIL IMPLEMENTATION.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>CONVERT_DATETIME_TO_TIMESTAMP
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_DATETIME TYPE ZCL_IBMX_SERVICE=>DATETIME
+* | [<-()] E_TIMESTAMP TYPE TIMESTAMP
+* +--------------------------------------------------------------------------------------
+ method convert_datetime_to_timestamp.
+
+ constants:
+ c_zero type timestamp value '0'. " avoid conversion at runtime
+
+ " check if input string applies to schema yyyy-mm-ddThh:mm:ssZ
+ if i_datetime cp '++++-++-++T++:++:++*'.
+ try.
+ e_timestamp =
+ i_datetime(4) * 10000000000 + i_datetime+5(2) * 100000000 + i_datetime+8(2) * 1000000 +
+ i_datetime+11(2) * 10000 + i_datetime+14(2) * 100 + i_datetime+17(2).
+ catch cx_sy_conversion_no_number.
+ " invalid input
+ e_timestamp = c_zero.
+ endtry.
+ else.
+ " invalid input
+ e_timestamp = c_zero.
+ endif.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>CONVERT_TIMESTAMP_TO_DATETIME
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_TIMESTAMP TYPE TIMESTAMP
+* | [<-()] E_DATETIME TYPE ZCL_IBMX_SERVICE=>DATETIME
+* +--------------------------------------------------------------------------------------
+ method convert_timestamp_to_datetime.
+
+ data:
+ lv_year(4) type n,
+ lv_month(2) type n,
+ lv_day(2) type n,
+ lv_hour(2) type n,
+ lv_min(2) type n,
+ lv_sec(2) type n,
+ lv_rest type timestamp.
+
+ lv_rest = i_timestamp.
+ lv_year = lv_rest div 10000000000.
+ lv_rest = lv_rest - ( lv_year * 10000000000 ).
+ lv_month = lv_rest div 100000000.
+ lv_rest = lv_rest - ( lv_month * 100000000 ).
+ lv_day = lv_rest div 1000000.
+ lv_rest = lv_rest - ( lv_day * 1000000 ).
+ lv_hour = lv_rest div 10000.
+ lv_rest = lv_rest - ( lv_hour * 10000 ).
+ lv_min = lv_rest div 100.
+ lv_rest = lv_rest - ( lv_min * 100 ).
+ lv_sec = lv_rest div 1. " type conversion
+
+ concatenate lv_year '-' lv_month '-' lv_day 'T' lv_hour ':' lv_min ':' lv_sec 'Z' into e_datetime.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>CONVERT_TIMESTAMP_TO_LOCAL
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] IV_TIMESTAMP TYPE TIMESTAMP
+* | [<-()] EV_LOCAL TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method convert_timestamp_to_local.
+
+ data:
+ lv_dats type d,
+ lv_tims type t,
+ lv_timezone type ZIF_IBMX_service_arch=>ty_timezone,
+ lv_datc(10) type c,
+ lv_timc(8) type c.
+
+ " split timestamp to date and time according to time zone
+ lv_timezone = ZCL_IBMX_service_arch=>get_timezone( ).
+ convert time stamp iv_timestamp time zone lv_timezone
+ into date lv_dats time lv_tims.
+
+ " write date and time to string using local date/time format
+ lv_datc = conv #( lv_dats ).
+ lv_timc = conv #( lv_tims ).
+ concatenate lv_datc lv_timc into ev_local separated by space.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>GET_MIMETYPE_FROM_EXTENSION
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_FILENAME TYPE STRING
+* | [<-()] E_MIMETYPE TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method get_mimetype_from_extension.
+ data:
+ l_extension type string.
+
+ if i_filename is initial.
+ e_mimetype = ZIF_IBMX_service_arch=>c_mediatype-all.
+ exit.
+ endif.
+
+ "find regex '\.([^\.]*)$' in i_filename submatches l_extension.
+ data(l_subrc) = ZCL_IBMX_service=>find_regex(
+ exporting
+ i_regex = '\.([^\.]*)$'
+ i_in = i_filename
+ changing
+ c_submatch1 = l_extension ).
+ if l_subrc <> 0.
+ l_extension = i_filename.
+ endif.
+
+ translate l_extension to lower case.
+
+ case l_extension.
+ when 'jpg' or 'jpeg'. e_mimetype = ZIF_IBMX_service_arch=>c_mediatype-image_jpeg.
+ when 'png'. e_mimetype = ZIF_IBMX_service_arch=>c_mediatype-image_png.
+ when 'txt'. e_mimetype = ZIF_IBMX_service_arch=>c_mediatype-text_plain.
+ when 'csv'. e_mimetype = ZIF_IBMX_service_arch=>c_mediatype-text_csv.
+ when others. e_mimetype = `application/` && l_extension ##NO_TEXT.
+ endcase.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>ITAB_TO_TABLESCHEMA
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_ITAB TYPE ANY TABLE
+* | [--->] I_DICTIONARY TYPE ANY(optional)
+* | [--->] I_LOWER_CASE TYPE ZCL_IBMX_SERVICE=>BOOLEAN (default =ZCL_IBMX_SERVICE=>C_BOOLEAN_FALSE)
+* | [--->] IT_EXCLUDED_FIELDS TYPE ZCL_IBMX_SERVICE=>TT_STRING(optional)
+* | [<-()] E_JSON TYPE STRING
+* +--------------------------------------------------------------------------------------
+ method itab_to_tableschema.
+ " Converts an internal table to a JSON object string with keys "fields" and "values".
+ " Example: I_ITAB = | PET | NUMBER |
+ " -------------------
+ " | Cat | 5 |
+ " | Rabbit | 2 |
+ " --> E_JSON = '{"fields": ["PET", "NUMBER"], "values": [["Cat",5],["Rabbit",2]]}'
+ " Field names are mapped according to I_DICTIONARY.
+ " Example: BEGIN OF i_dictionary,
+ " PET type string value 'MyPet',
+ " END OF i_dictionary.
+ " --> E_JSON = '{"fields": ["MyPet", "NUMBER"], "values": [["Cat",5],["Rabbit",2]]}'
+ " Field names are translated to lower case, if I_LOWER_CASE = ZCL_IBMX_service=>c_boolean_true.
+ " Field types must be elementary or table of elementary.
+ " Field names in table IT_EXCLUDED_FIELDS are skipped and will not appear in the JSON string.
+
+ data:
+ lt_comp type cl_abap_structdescr=>component_table,
+ ls_comp like line of lt_comp,
+ lo_datadescr type ref to cl_abap_datadescr,
+ lo_tabledesc type ref to cl_abap_tabledescr,
+ lo_struct type ref to cl_abap_structdescr,
+ lv_fieldname type string,
+ lv_sep1(1) type c,
+ lv_sep2(1) type c,
+ lv_sep3(1) type c.
+
+ field-symbols:
+ type any,
+ type any,
+ type any table,
+ type string.
+
+ " the following data type are character-like, thus the according values must be quoted
+ data(lc_character_types) =
+ cl_abap_datadescr=>typekind_char &&
+ cl_abap_datadescr=>typekind_clike &&
+ cl_abap_datadescr=>typekind_csequence &&
+ cl_abap_datadescr=>typekind_string &&
+ cl_abap_datadescr=>typekind_date &&
+ cl_abap_datadescr=>typekind_time &&
+ cl_abap_datadescr=>typekind_num.
+
+ " get internal table fields
+ lo_tabledesc ?= cl_abap_tabledescr=>describe_by_data( i_itab ).
+ lo_struct ?= lo_tabledesc->get_table_line_type( ).
+ lt_comp = lo_struct->get_components( ).
+
+ " append field names to JSON string
+ e_json = `{"fields": [` ##NO_TEXT.
+ clear lv_sep1.
+ loop at lt_comp into ls_comp.
+ read table it_excluded_fields with key table_line = ls_comp-name transporting no fields.
+ if sy-subrc = 0. continue. endif.
+ lv_fieldname = ls_comp-name.
+ if i_dictionary is supplied.
+ assign component ls_comp-name of structure i_dictionary to .
+ if sy-subrc = 0.
+ lv_fieldname = .
+ endif.
+ endif.
+ if i_lower_case eq ZCL_IBMX_service=>c_boolean_true.
+ translate lv_fieldname to lower case.
+ endif.
+ e_json = e_json && lv_sep1 && `"` && lv_fieldname && `"`.
+ lv_sep1 = `,`.
+ endloop.
+
+ " append values to JSON string
+ e_json = e_json && `], "values": [` ##NO_TEXT.
+
+ clear lv_sep1.
+ loop at i_itab assigning . " loop on itab records
+ clear lv_sep2.
+ e_json = e_json && lv_sep1 && `[`.
+ loop at lt_comp into ls_comp. " loop on record fields
+ read table it_excluded_fields with key table_line = ls_comp-name transporting no fields.
+ if sy-subrc = 0. continue. endif.
+
+ if ls_comp-type->type_kind eq cl_abap_datadescr=>typekind_table.
+
+ " field type is subtable, get line type
+ assign component ls_comp-name of structure to .
+ lo_tabledesc ?= cl_abap_tabledescr=>describe_by_data( ).
+ lo_datadescr = lo_tabledesc->get_table_line_type( ).
+
+ " add field values of subtable to JSON string
+ clear lv_sep3.
+ e_json = e_json && lv_sep2 && `[`.
+ loop at assigning .
+ if lo_datadescr->type_kind ca lc_character_types.
+ e_json = e_json && lv_sep3 && `"` && && `"`.
+ else.
+ e_json = e_json && lv_sep3 && .
+ endif.
+ lv_sep3 = `,`.
+ endloop.
+ e_json = e_json && `]`.
+
+ else.
+
+ " field type is elementary, add field value to JSON string
+ assign component ls_comp-name of structure to .
+ if ls_comp-type->type_kind ca lc_character_types.
+ e_json = e_json && lv_sep2 && `"` && && `"`.
+ else.
+ e_json = e_json && lv_sep2 && .
+ endif.
+
+ endif.
+
+ lv_sep2 = `,`.
+ endloop.
+
+ e_json = e_json && `]`.
+ lv_sep1 = `,`.
+ endloop.
+
+ e_json = e_json && `]}` ##NO_TEXT.
+
+ endmethod.
+
+
+* ---------------------------------------------------------------------------------------+
+* | Static Public Method ZCL_IBMX_UTIL=>TABLESCHEMA_TO_ITAB
+* +-------------------------------------------------------------------------------------------------+
+* | [--->] I_JSON TYPE STRING
+* | [--->] I_TABLESCHEMA_KEY TYPE STRING(optional)
+* | [<---] E_ITAB TYPE ANY TABLE
+* +--------------------------------------------------------------------------------------
+ method tableschema_to_itab.
+
+ data:
+ lt_comp type cl_abap_structdescr=>component_table,
+ ls_comp like line of lt_comp,
+ lv_field type string,
+ lt_field type standard table of string,
+ lv_fieldname type string,
+ lv_tabix type i,
+ lv_tableschema_key type string,
+ lo_structdescr type ref to cl_abap_structdescr,
+ lo_datadescr type ref to cl_abap_datadescr,
+ lo_tabledescr type ref to cl_abap_tabledescr,
+ lr_values type ref to data,
+ lr_comp type ref to data,
+ lr_data type ref to data,
+ lv_json type string.
+
+ field-symbols:
+ type ref to data,
+ type any table,
+ type any,
+ type any,
+