CLASS zcl_sapilot_http_handler DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_http_extension. PRIVATE SECTION. METHODS process_request IMPORTING io_server TYPE REF TO if_http_server RAISING zcx_sapilot_error. METHODS set_response_headers IMPORTING io_server TYPE REF TO if_http_server. METHODS send_error_response IMPORTING io_server TYPE REF TO if_http_server ix_error TYPE REF TO zcx_sapilot_error. METHODS send_success_response IMPORTING io_server TYPE REF TO if_http_server iv_json TYPE string iv_status TYPE i DEFAULT 200. METHODS get_client_ip IMPORTING io_server TYPE REF TO if_http_server RETURNING VALUE(rv_ip) TYPE string. ENDCLASS. CLASS zcl_sapilot_http_handler IMPLEMENTATION. METHOD if_http_extension~handle_request. DATA: lx_error TYPE REF TO zcx_sapilot_error. DATA: lx_unexpected TYPE REF TO cx_static_check. DATA: lo_unexpected TYPE REF TO zcx_sapilot_error. TRY. process_request( server ). CATCH zcx_sapilot_error INTO lx_error. send_error_response( io_server = server ix_error = lx_error ). CATCH cx_static_check INTO lx_unexpected. CREATE OBJECT lo_unexpected EXPORTING iv_http_status = 500 iv_error_code = 'INTERNAL_ERROR' iv_message = 'An unexpected error occurred' iv_details = lx_unexpected->get_text( ). send_error_response( io_server = server ix_error = lo_unexpected ). ENDTRY. ENDMETHOD. METHOD process_request. DATA: lo_router TYPE REF TO zcl_sapilot_router, lo_response_builder TYPE REF TO zcl_sapilot_response, lo_audit TYPE REF TO zcl_sapilot_audit_logger, lo_health TYPE REF TO zcl_sapilot_op_health, lo_getlist TYPE REF TO zcl_sapilot_op_getlist, lo_getdetail TYPE REF TO zcl_sapilot_op_getdetail, lo_use TYPE REF TO zcl_sapilot_op_use, lo_logs TYPE REF TO zcl_sapilot_op_logs, lo_admin TYPE REF TO zcl_sapilot_op_admin. CREATE OBJECT lo_router. CREATE OBJECT lo_response_builder. CREATE OBJECT lo_audit. GET TIME STAMP FIELD DATA(lv_start_ts). DATA(lv_request_id) = lo_response_builder->generate_request_id( ). DATA(lv_method) = CONV string( io_server->request->get_method( ) ). DATA(lv_path) = CONV string( io_server->request->get_header_field( '~path_info' ) ). DATA(lv_query) = CONV string( io_server->request->get_header_field( '~query_string' ) ). DATA(lv_body) = io_server->request->get_cdata( ). DATA(ls_route) = lo_router->route( iv_method = lv_method iv_path = lv_path ). DATA: lv_result_json TYPE string. DATA: lx_route TYPE REF TO zcx_sapilot_error. CASE ls_route-operation. WHEN zcl_sapilot_constants=>gc_op_health. CREATE OBJECT lo_health. lv_result_json = lo_health->execute( ). WHEN zcl_sapilot_constants=>gc_op_get_list. CREATE OBJECT lo_getlist. lv_result_json = lo_getlist->execute( iv_query_string = lv_query ). WHEN zcl_sapilot_constants=>gc_op_get_detail. CREATE OBJECT lo_getdetail. lv_result_json = lo_getdetail->execute( iv_path_info = lv_path ). WHEN zcl_sapilot_constants=>gc_op_use. CREATE OBJECT lo_use. lv_result_json = lo_use->execute( iv_path_info = lv_path iv_request_body = lv_body ). WHEN zcl_sapilot_constants=>gc_op_logs. CREATE OBJECT lo_logs. lv_result_json = lo_logs->execute( iv_query_string = lv_query ). WHEN zcl_sapilot_constants=>gc_op_admin_reg OR zcl_sapilot_constants=>gc_op_admin_upd OR zcl_sapilot_constants=>gc_op_admin_del OR zcl_sapilot_constants=>gc_op_admin_act. CREATE OBJECT lo_admin. lv_result_json = lo_admin->execute( iv_operation = ls_route-operation iv_object_type = CONV string( ls_route-object_type ) iv_object_name = CONV string( ls_route-object_name ) iv_request_body = lv_body ). WHEN OTHERS. RAISE EXCEPTION TYPE zcx_sapilot_error EXPORTING iv_http_status = 404 iv_error_code = 'ROUTE_NOT_FOUND' iv_message = |No handler for path: { lv_path }|. ENDCASE. GET TIME STAMP FIELD DATA(lv_end_ts). DATA: lv_duration_ms TYPE i. lv_duration_ms = cl_abap_tstmp=>subtract( tstmp1 = lv_end_ts tstmp2 = lv_start_ts ). DATA(lv_response) = lo_response_builder->build_success( iv_json = lv_result_json iv_request_id = lv_request_id iv_duration = |{ lv_duration_ms }ms| ). TRY. lo_audit->log( iv_request_id = lv_request_id iv_operation = ls_route-operation iv_object_type = CONV string( ls_route-object_type ) iv_object_name = CONV string( ls_route-object_name ) iv_input_json = lv_body iv_status = zcl_sapilot_constants=>gc_status_success iv_duration_ms = lv_duration_ms iv_remote_ip = get_client_ip( io_server ) ). CATCH cx_static_check. ENDTRY. send_success_response( io_server = io_server iv_json = lv_response ). ENDMETHOD. METHOD set_response_headers. DATA(lo_response) = io_server->response. lo_response->set_content_type( zcl_sapilot_constants=>gc_content_type ). lo_response->set_header_field( name = 'charset' value = zcl_sapilot_constants=>gc_charset ). lo_response->set_header_field( name = 'Access-Control-Allow-Origin' value = '*' ). lo_response->set_header_field( name = 'Access-Control-Allow-Methods' value = 'GET, POST, PUT, DELETE, OPTIONS' ). lo_response->set_header_field( name = 'Access-Control-Allow-Headers' value = 'Content-Type, Authorization' ). ENDMETHOD. METHOD send_error_response. DATA: lo_response_builder TYPE REF TO zcl_sapilot_response. DATA: lv_request_id TYPE string. DATA: lv_duration TYPE string. DATA: lv_json TYPE string. DATA: lv_error_json TYPE string. DATA: lv_metadata TYPE string. CREATE OBJECT lo_response_builder. lv_request_id = lo_response_builder->generate_request_id( ). lv_duration = '0ms'. lv_error_json = ix_error->get_json_error( ). lv_metadata = lo_response_builder->build_metadata( iv_request_id = lv_request_id iv_duration = lv_duration ). DATA: lv_open TYPE c LENGTH 1, lv_close TYPE string. lv_open = cl_abap_char_utilities=>horizontal_tab. FIELD-SYMBOLS: TYPE x. ASSIGN lv_open TO CASTING. = '7B'. lv_close = '}'. CONCATENATE lv_open '"success": false, "error": ' lv_error_json ', "metadata": ' lv_metadata lv_close INTO lv_json. set_response_headers( io_server ). io_server->response->set_status( code = ix_error->mv_http_status reason = ix_error->mv_error_code ). io_server->response->set_cdata( lv_json ). ENDMETHOD. METHOD send_success_response. set_response_headers( io_server ). io_server->response->set_status( code = iv_status reason = 'OK' ). io_server->response->set_cdata( iv_json ). ENDMETHOD. METHOD get_client_ip. DATA: lv_fwd TYPE string, lv_real TYPE string. lv_fwd = io_server->request->get_header_field( 'X-Forwarded-For' ). IF lv_fwd IS NOT INITIAL. rv_ip = lv_fwd. ELSE. lv_real = io_server->request->get_header_field( 'X-Real-IP' ). IF lv_real IS NOT INITIAL. rv_ip = lv_real. ELSE. rv_ip = io_server->request->get_header_field( 'REMOTE_ADDR' ). ENDIF. ENDIF. ENDMETHOD. ENDCLASS.