*&---------------------------------------------------------------------* *& Include ZABAPGIT_TRANSPORT *&---------------------------------------------------------------------* CLASS lcl_transport DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: zip RAISING lcx_exception, to_tadir IMPORTING it_transport_headers TYPE trwbo_request_headers RETURNING VALUE(rt_tadir) TYPE scts_tadir RAISING lcx_exception. PRIVATE SECTION. CLASS-METHODS: read_requests IMPORTING it_trkorr TYPE trwbo_request_headers RETURNING VALUE(rt_requests) TYPE trwbo_requests RAISING lcx_exception, find_top_package IMPORTING it_tadir TYPE scts_tadir RETURNING VALUE(rv_package) TYPE devclass, resolve IMPORTING it_requests TYPE trwbo_requests RETURNING VALUE(rt_tadir) TYPE scts_tadir RAISING lcx_exception. ENDCLASS. CLASS lcl_transport IMPLEMENTATION. METHOD zip. DATA: lt_requests TYPE trwbo_requests, lt_tadir TYPE scts_tadir, lv_package TYPE devclass, ls_data TYPE lcl_persistence_repo=>ty_repo, lo_repo TYPE REF TO lcl_repo_offline, lt_trkorr TYPE trwbo_request_headers. lt_trkorr = lcl_popups=>popup_to_select_transports( ). IF lines( lt_trkorr ) = 0. RETURN. ENDIF. lt_requests = read_requests( lt_trkorr ). lt_tadir = resolve( lt_requests ). IF lines( lt_tadir ) = 0. lcx_exception=>raise( 'empty transport' ). ENDIF. lv_package = find_top_package( lt_tadir ). IF lv_package IS INITIAL. lcx_exception=>raise( 'error finding super package' ). ENDIF. ls_data-key = 'TZIP'. ls_data-package = lv_package. ls_data-dot_abapgit = lcl_dot_abapgit=>build_default( )->get_data( ). CREATE OBJECT lo_repo EXPORTING is_data = ls_data. lcl_zip=>export( io_repo = lo_repo it_filter = lt_tadir ). ENDMETHOD. METHOD to_tadir. DATA: lt_requests TYPE trwbo_requests, lt_tadir TYPE scts_tadir, lv_package TYPE devclass, lt_trkorr TYPE trwbo_request_headers. IF lines( it_transport_headers ) = 0. RETURN. ENDIF. lt_requests = read_requests( it_transport_headers ). rt_tadir = resolve( lt_requests ). ENDMETHOD. METHOD find_top_package. * assumption: all objects in transport share a common super package DATA: lt_obj TYPE lif_sap_package=>ty_devclass_tt, lt_super TYPE lif_sap_package=>ty_devclass_tt, lv_super LIKE LINE OF lt_super, lv_index TYPE i. FIELD-SYMBOLS: LIKE LINE OF it_tadir. READ TABLE it_tadir INDEX 1 ASSIGNING . ASSERT sy-subrc = 0. lt_super = lcl_sap_package=>get( -devclass )->list_superpackages( ). LOOP AT it_tadir ASSIGNING . lt_obj = lcl_sap_package=>get( -devclass )->list_superpackages( ). * filter out possibilities from lt_super LOOP AT lt_super INTO lv_super. lv_index = sy-tabix. READ TABLE lt_obj FROM lv_super TRANSPORTING NO FIELDS. IF sy-subrc <> 0. DELETE lt_super INDEX lv_index. ENDIF. ENDLOOP. ENDLOOP. SORT lt_super. READ TABLE lt_super INDEX 1 INTO rv_package. ENDMETHOD. METHOD read_requests. DATA lt_requests LIKE rt_requests. FIELD-SYMBOLS LIKE LINE OF it_trkorr. LOOP AT it_trkorr ASSIGNING . CALL FUNCTION 'TR_READ_REQUEST_WITH_TASKS' EXPORTING iv_trkorr = -trkorr IMPORTING et_requests = lt_requests EXCEPTIONS invalid_input = 1 OTHERS = 2. IF sy-subrc <> 0. lcx_exception=>raise( 'error from TR_READ_REQUEST_WITH_TASKS' ). ENDIF. APPEND LINES OF lt_requests TO rt_requests. ENDLOOP. ENDMETHOD. METHOD resolve. DATA: lv_object TYPE tadir-object, lv_obj_name TYPE tadir-obj_name, lv_trobj_name TYPE trobj_name, ls_tadir TYPE tadir. FIELD-SYMBOLS: LIKE LINE OF it_requests, LIKE LINE OF -objects. LOOP AT it_requests ASSIGNING . LOOP AT -objects ASSIGNING . IF -pgmid = 'LIMU'. CALL FUNCTION 'GET_R3TR_OBJECT_FROM_LIMU_OBJ' EXPORTING p_limu_objtype = -object p_limu_objname = -obj_name IMPORTING p_r3tr_objtype = lv_object p_r3tr_objname = lv_trobj_name EXCEPTIONS no_mapping = 1 OTHERS = 2. IF sy-subrc <> 0. lcx_exception=>raise( 'error from GET_R3TR_OBJECT_FROM_LIMU_OBJ' ). ENDIF. lv_obj_name = lv_trobj_name. ELSE. lv_object = -object. lv_obj_name = -obj_name. ENDIF. ls_tadir = lcl_tadir=>read_single( iv_object = lv_object iv_obj_name = lv_obj_name ). APPEND ls_tadir TO rt_tadir. ENDLOOP. ENDLOOP. SORT rt_tadir BY object ASCENDING obj_name ASCENDING. DELETE ADJACENT DUPLICATES FROM rt_tadir COMPARING object obj_name. DELETE rt_tadir WHERE table_line IS INITIAL. ENDMETHOD. ENDCLASS. CLASS lcl_transport_objects DEFINITION. "Under test at ltcl_transport_objects PUBLIC SECTION. METHODS constructor IMPORTING it_transport_objects TYPE scts_tadir. METHODS to_stage IMPORTING io_stage TYPE REF TO lcl_stage is_stage_objects TYPE ty_stage_files it_object_statuses TYPE ty_results_tt RAISING lcx_exception. PRIVATE SECTION. DATA mt_transport_objects TYPE scts_tadir. ENDCLASS. CLASS lcl_transport_objects IMPLEMENTATION. METHOD constructor. mt_transport_objects = it_transport_objects. ENDMETHOD. METHOD to_stage. DATA: ls_transport_object TYPE tadir, ls_local_file TYPE ty_file_item, ls_object_status TYPE ty_result. LOOP AT mt_transport_objects INTO ls_transport_object. LOOP AT it_object_statuses INTO ls_object_status WHERE obj_name = ls_transport_object-obj_name AND obj_type = ls_transport_object-object. CASE ls_object_status-lstate. WHEN gc_state-added OR gc_state-modified. IF ls_transport_object-delflag = abap_true. lcx_exception=>raise( |Object { ls_transport_object-obj_name } should be added/modified, but has deletion flag in transport| ). ENDIF. READ TABLE is_stage_objects-local INTO ls_local_file WITH KEY item-obj_name = ls_transport_object-obj_name item-obj_type = ls_transport_object-object file-filename = ls_object_status-filename. IF sy-subrc <> 0. lcx_exception=>raise( |Object { ls_transport_object-obj_name } not found in the local repository files| ). ENDIF. io_stage->add( iv_path = ls_local_file-file-path iv_filename = ls_local_file-file-filename iv_data = ls_local_file-file-data ). WHEN gc_state-deleted. IF ls_transport_object-delflag = abap_false. lcx_exception=>raise( |Object { ls_transport_object-obj_name } should be removed, but has NO deletion flag in transport| ). ENDIF. io_stage->rm( iv_path = ls_object_status-path iv_filename = ls_object_status-filename ). WHEN OTHERS. ASSERT 0 = 1. "Unexpected state ENDCASE. ENDLOOP. IF sy-subrc <> 0. lcx_exception=>raise( |Object { ls_transport_object-obj_name } not found in the local repository files| ). ENDIF. ENDLOOP. ENDMETHOD. ENDCLASS. CLASS lcl_transport_to_branch DEFINITION. PUBLIC SECTION. METHODS: create IMPORTING io_repository TYPE REF TO lcl_repo_online is_transport_to_branch TYPE ty_transport_to_branch it_transport_objects TYPE scts_tadir RAISING lcx_exception. PRIVATE SECTION. METHODS create_new_branch IMPORTING io_repository TYPE REF TO lcl_repo_online iv_branch_name TYPE string RAISING lcx_exception. METHODS generate_commit_message IMPORTING is_transport_to_branch TYPE ty_transport_to_branch RETURNING VALUE(rs_comment) TYPE ty_comment. METHODS stage_transport_objects IMPORTING it_transport_objects TYPE scts_tadir io_stage TYPE REF TO lcl_stage is_stage_objects TYPE ty_stage_files it_object_statuses TYPE ty_results_tt RAISING lcx_exception. ENDCLASS. CLASS lcl_transport_to_branch IMPLEMENTATION. METHOD create. DATA: ls_transport_object TYPE LINE OF scts_tadir, lt_items TYPE ty_files_item_tt, ls_local_file TYPE LINE OF ty_files_item_tt, ls_remote_file TYPE LINE OF ty_files_tt, ls_item TYPE string, lv_branch_name TYPE string, ls_comment TYPE ty_comment, lo_stage TYPE REF TO lcl_stage, ls_stage_objects TYPE ty_stage_files, lt_object_statuses TYPE ty_results_tt, ls_object_status TYPE LINE OF ty_results_tt. lv_branch_name = lcl_git_branch_list=>complete_heads_branch_name( lcl_git_branch_list=>normalize_branch_name( is_transport_to_branch-branch_name ) ). create_new_branch( io_repository = io_repository iv_branch_name = lv_branch_name ). CREATE OBJECT lo_stage EXPORTING iv_branch_name = lv_branch_name iv_branch_sha1 = io_repository->get_sha1_remote( ). ls_stage_objects = lcl_stage_logic=>get( io_repository ). lt_object_statuses = io_repository->status( ). stage_transport_objects( it_transport_objects = it_transport_objects io_stage = lo_stage is_stage_objects = ls_stage_objects it_object_statuses = lt_object_statuses ). ls_comment = generate_commit_message( is_transport_to_branch ). io_repository->push( is_comment = ls_comment io_stage = lo_stage ). ENDMETHOD. METHOD create_new_branch. ASSERT iv_branch_name CP 'refs/heads/+*'. TRY. lcl_git_porcelain=>create_branch( io_repo = io_repository iv_name = iv_branch_name iv_from = io_repository->get_sha1_local( ) ). io_repository->set_branch_name( iv_branch_name ). CATCH lcx_exception. lcx_exception=>raise( 'Error when creating new branch'). ENDTRY. ENDMETHOD. METHOD generate_commit_message. rs_comment-committer-name = sy-uname. rs_comment-committer-email = |{ rs_comment-committer-name }@localhost|. rs_comment-comment = is_transport_to_branch-commit_text. ENDMETHOD. METHOD stage_transport_objects. DATA lo_transport_objects type ref to lcl_transport_objects. CREATE OBJECT lo_transport_objects EXPORTING it_transport_objects = it_transport_objects. lo_transport_objects->to_stage( io_stage = io_stage is_stage_objects = is_stage_objects it_object_statuses = it_object_statuses ). ENDMETHOD. ENDCLASS. CLASS ltcl_transport_objects DEFINITION FOR TESTING. PRIVATE SECTION. METHODS: add_new_to_local_files FOR TESTING RAISING cx_static_check, modified_to_new_local_files FOR TESTING RAISING cx_static_check, transport_not_in_repository FOR TESTING RAISING cx_static_check, object_not_in_local_files FOR TESTING RAISING cx_static_check, cant_be_added_with_del_flag FOR TESTING RAISING cx_static_check, cant_be_modified_with_del_flag FOR TESTING RAISING cx_static_check, deleted_to_removed_files FOR TESTING RAISING cx_static_check, shouldnt_remove_no_delflag FOR TESTING RAISING cx_static_check, should_add_all_local_files FOR TESTING RAISING cx_static_check, should_delete_all_related FOR TESTING RAISING cx_static_check, setup, given_the_transport_object IMPORTING iv_obj_name TYPE string iv_obj_type TYPE string iv_delflag TYPE abap_bool OPTIONAL, given_the_object_status IMPORTING iv_obj_name TYPE string OPTIONAL iv_obj_type TYPE string OPTIONAL iv_filename TYPE string OPTIONAL iv_path TYPE string OPTIONAL iv_lstate TYPE char1, given_the_local_file IMPORTING iv_obj_name TYPE string iv_obj_type TYPE string iv_filename TYPE string iv_path TYPE string iv_data TYPE string RETURNING VALUE(rs_local_file) TYPE ty_file_item, when_staging RAISING lcx_exception, then_file_should_be_added IMPORTING is_local_file TYPE ty_file_item, then_it_should_raise_exception IMPORTING with_text TYPE string, then_it_should_remove_at_stage IMPORTING iv_filename TYPE string IV_PATH TYPE STRING. DATA: mo_transport_objects TYPE REF TO lcl_transport_objects, mt_transport_objects TYPE scts_tadir, mt_object_statuses TYPE ty_results_tt, ms_stage_objects TYPE ty_stage_files, mo_stage TYPE REF TO lcl_stage. ENDCLASS. CLASS ltcl_transport_objects IMPLEMENTATION. METHOD setup. CREATE OBJECT mo_stage EXPORTING iv_branch_name = 'A_branch_name' iv_branch_sha1 = 'Branch_SH1'. ENDMETHOD. METHOD add_new_to_local_files. DATA ls_local_file TYPE ty_file_item. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_lstate = gc_state-added ). ls_local_file = given_the_local_file( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/path' iv_data = 'data' ). when_staging( ). then_file_should_be_added( ls_local_file ). ENDMETHOD. METHOD modified_to_new_local_files. DATA ls_local_file TYPE ty_file_item. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_lstate = gc_state-modified ). ls_local_file = given_the_local_file( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/path' iv_data = 'data' ). when_staging( ). then_file_should_be_added( ls_local_file ). ENDMETHOD. METHOD should_add_all_local_files. "Not only .abap, but also .xml and other includes DATA ls_abap_local_file TYPE ty_file_item. DATA ls_xml_local_file TYPE ty_file_item. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_lstate = gc_state-modified ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.xml' iv_lstate = gc_state-modified ). ls_abap_local_file = given_the_local_file( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/path' iv_data = 'data' ). ls_xml_local_file = given_the_local_file( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.xml' iv_path = '/path' iv_data = 'data' ). when_staging( ). then_file_should_be_added( ls_abap_local_file ). then_file_should_be_added( ls_xml_local_file ). ENDMETHOD. METHOD transport_not_in_repository. given_the_transport_object( iv_obj_name = 'CL_A_CLASS_NOT_IN_REPO' iv_obj_type = 'CLAS' ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_lstate = gc_state-added ). then_it_should_raise_exception( with_text = 'Object CL_A_CLASS_NOT_IN_REPO not found in the local repository files' ). ENDMETHOD. METHOD object_not_in_local_files. DATA ls_local_file TYPE ty_file_item. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_lstate = gc_state-added ). given_the_local_file( iv_obj_name = 'CL_ANOTHER_LOCAL_FILE' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/path' iv_data = 'data' ). then_it_should_raise_exception( with_text = 'Object CL_FOO not found in the local repository files' ). ENDMETHOD. METHOD cant_be_added_with_del_flag. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_delflag = abap_true ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_lstate = gc_state-added ). then_it_should_raise_exception( with_text = 'Object CL_FOO should be added/modified, but has deletion flag in transport' ). ENDMETHOD. METHOD cant_be_modified_with_del_flag. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_delflag = abap_true ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_lstate = gc_state-modified ). then_it_should_raise_exception( with_text = 'Object CL_FOO should be added/modified, but has deletion flag in transport' ). ENDMETHOD. METHOD deleted_to_removed_files. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_delflag = abap_true ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/a_path' iv_lstate = gc_state-deleted ). when_staging( ). then_it_should_remove_at_stage( iv_filename = 'CL_FOO.abap' iv_path = '/a_path' ). ENDMETHOD. METHOD should_delete_all_related. "i.e. Should also delete the XMLs related to the transport objects given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_delflag = abap_true ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/a_path' iv_lstate = gc_state-deleted ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.xml' iv_path = '/a_path' iv_lstate = gc_state-deleted ). when_staging( ). then_it_should_remove_at_stage( iv_filename = 'CL_FOO.abap' iv_path = '/a_path' ). then_it_should_remove_at_stage( iv_filename = 'CL_FOO.xml' iv_path = '/a_path' ). ENDMETHOD. METHOD shouldnt_remove_no_delflag. given_the_transport_object( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_delflag = abap_false ). given_the_object_status( iv_obj_name = 'CL_FOO' iv_obj_type = 'CLAS' iv_filename = 'CL_FOO.abap' iv_path = '/a_path' iv_lstate = gc_state-deleted ). then_it_should_raise_exception( with_text = 'Object CL_FOO should be removed, but has NO deletion flag in transport' ). ENDMETHOD. METHOD given_the_transport_object. DATA ls_transport_object TYPE tadir. ls_transport_object-obj_name = iv_obj_name. ls_transport_object-object = iv_obj_type. ls_transport_object-delflag = iv_delflag. APPEND ls_transport_object TO mt_transport_objects. ENDMETHOD. METHOD given_the_object_status. DATA ls_object_status TYPE ty_result. ls_object_status-obj_name = iv_obj_name. ls_object_status-obj_type = iv_obj_type. ls_object_status-filename = iv_filename. ls_object_status-path = iv_path. ls_object_status-lstate = iv_lstate. APPEND ls_object_status TO mt_object_statuses. ENDMETHOD. METHOD given_the_local_file. rs_local_file-item-obj_name = iv_obj_name. rs_local_file-item-obj_type = iv_obj_type. rs_local_file-file-filename = iv_filename. rs_local_file-file-path = iv_path. rs_local_file-file-data = iv_data. APPEND rs_local_file TO ms_stage_objects-local. ENDMETHOD. METHOD when_staging. CREATE OBJECT mo_transport_objects EXPORTING it_transport_objects = mt_transport_objects. mo_transport_objects->to_stage( io_stage = mo_stage is_stage_objects = ms_stage_objects it_object_statuses = mt_object_statuses ). ENDMETHOD. METHOD then_file_should_be_added. DATA: lt_staged_objects TYPE lcl_stage=>ty_stage_tt. lt_staged_objects = mo_stage->get_all( ). READ TABLE lt_staged_objects TRANSPORTING NO FIELDS WITH KEY file-filename = is_local_file-file-filename file-path = is_local_file-file-path file-data = is_local_file-file-data method = lcl_stage=>c_method-add. IF sy-subrc <> 0. cl_abap_unit_assert=>fail( |Object { is_local_file-file-filename } not added to stage| ). ENDIF. ENDMETHOD. METHOD then_it_should_raise_exception. DATA: lo_exception TYPE REF TO lcx_exception. TRY. when_staging( ). cl_abap_unit_assert=>fail( 'Should have raised exception'). CATCH lcx_exception INTO lo_exception. cl_abap_unit_assert=>assert_equals( act = lo_exception->mv_text exp = with_text ). ENDTRY. ENDMETHOD. METHOD then_it_should_remove_at_stage. DATA: lt_staged_objects TYPE lcl_stage=>ty_stage_tt. lt_staged_objects = mo_stage->get_all( ). READ TABLE lt_staged_objects TRANSPORTING NO FIELDS WITH KEY file-filename = iv_filename file-path = iv_path. IF sy-subrc <> 0. cl_abap_unit_assert=>fail( |Object { iv_filename } not removed in stage| ). ENDIF. ENDMETHOD. ENDCLASS.