diff --git a/.doc_gen/metadata/s3_metadata.yaml b/.doc_gen/metadata/s3_metadata.yaml index 0ffc3b3a788..e6018d586a3 100644 --- a/.doc_gen/metadata/s3_metadata.yaml +++ b/.doc_gen/metadata/s3_metadata.yaml @@ -2462,6 +2462,14 @@ s3_ListBuckets: snippet_tags: - s3.swift.import - s3.swift.listbuckets.ListBuckets + SAP ABAP: + versions: + - sdk_version: 1 + github: sap-abap/services/s3 + excerpts: + - description: + snippet_tags: + - s3.abapv1.list_buckets services: s3: {ListBuckets} s3_PutObjectAcl: diff --git a/sap-abap/services/s3/#awsex#cl_s3_actions.clas.abap b/sap-abap/services/s3/#awsex#cl_s3_actions.clas.abap index 61e75485039..d9783bccd43 100644 --- a/sap-abap/services/s3/#awsex#cl_s3_actions.clas.abap +++ b/sap-abap/services/s3/#awsex#cl_s3_actions.clas.abap @@ -176,6 +176,10 @@ CLASS /awsex/cl_s3_actions DEFINITION EXPORTING !oo_result TYPE REF TO /aws1/cl_s3_headbucketoutput RAISING /aws1/cx_rt_generic. + METHODS list_buckets + EXPORTING + !oo_result TYPE REF TO /aws1/cl_s3_listbucketsoutput + RAISING /aws1/cx_rt_generic. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. @@ -815,4 +819,23 @@ CLASS /AWSEX/CL_S3_ACTIONS IMPLEMENTATION. ENDTRY. " snippet-end:[s3.abapv1.head_bucket] ENDMETHOD. + + + METHOD list_buckets. + CONSTANTS cv_pfl TYPE /aws1/rt_profile_id VALUE 'ZCODE_DEMO'. + + DATA(lo_session) = /aws1/cl_rt_session_aws=>create( cv_pfl ). + DATA(lo_s3) = /aws1/cl_s3_factory=>create( lo_session ). + + " snippet-start:[s3.abapv1.list_buckets] + TRY. + oo_result = lo_s3->listbuckets( " oo_result is returned for testing purposes. " + ). + DATA(lv_bucket_count) = lines( oo_result->get_buckets( ) ). + MESSAGE |Retrieved { lv_bucket_count } buckets in all regions.| TYPE 'I'. + CATCH /aws1/cx_rt_generic. + MESSAGE 'Unable to list buckets.' TYPE 'E'. + ENDTRY. + " snippet-end:[s3.abapv1.list_buckets] + ENDMETHOD. ENDCLASS. diff --git a/sap-abap/services/s3/#awsex#cl_s3_actions.clas.testclasses.abap b/sap-abap/services/s3/#awsex#cl_s3_actions.clas.testclasses.abap index 3962a6bbd19..878dc80b7ae 100644 --- a/sap-abap/services/s3/#awsex#cl_s3_actions.clas.testclasses.abap +++ b/sap-abap/services/s3/#awsex#cl_s3_actions.clas.testclasses.abap @@ -49,7 +49,8 @@ CLASS ltc_awsex_cl_s3_actions DEFINITION FOR TESTING DURATION SHORT RISK LEVEL D put_object_legal_hold FOR TESTING RAISING /aws1/cx_rt_generic, put_object_retention FOR TESTING RAISING /aws1/cx_rt_generic, get_object_lock_conf FOR TESTING RAISING /aws1/cx_rt_generic, - put_object_lock_conf FOR TESTING RAISING /aws1/cx_rt_generic. + put_object_lock_conf FOR TESTING RAISING /aws1/cx_rt_generic, + list_buckets FOR TESTING RAISING /aws1/cx_rt_generic. CLASS-METHODS class_setup RAISING /aws1/cx_rt_generic /awsex/cx_generic. CLASS-METHODS class_teardown RAISING /aws1/cx_rt_generic /awsex/cx_generic. @@ -459,6 +460,8 @@ CLASS ltc_awsex_cl_s3_actions IMPLEMENTATION. DATA lt_cors_rules TYPE /aws1/cl_s3_corsrule=>tt_corsrules. DATA lt_methods TYPE /aws1/cl_s3_allowedmethods_w=>tt_allowedmethods. DATA lt_origins TYPE /aws1/cl_s3_allowedorigins_w=>tt_allowedorigins. + DATA lv_retry TYPE i. + DATA lv_cors_set TYPE abap_bool VALUE abap_false. APPEND NEW /aws1/cl_s3_allowedmethods_w( iv_value = 'GET' ) TO lt_methods. APPEND NEW /aws1/cl_s3_allowedorigins_w( iv_value = '*' ) TO lt_origins. @@ -467,30 +470,73 @@ CLASS ltc_awsex_cl_s3_actions IMPLEMENTATION. it_allowedmethods = lt_methods it_allowedorigins = lt_origins ) TO lt_cors_rules. - ao_s3->putbucketcors( - iv_bucket = av_bucket - io_corsconfiguration = NEW /aws1/cl_s3_corsconfiguration( it_corsrules = lt_cors_rules ) ). + " Set CORS configuration and retry if needed + DO 3 TIMES. + lv_retry = sy-index. + TRY. + ao_s3->putbucketcors( + iv_bucket = av_bucket + io_corsconfiguration = NEW /aws1/cl_s3_corsconfiguration( it_corsrules = lt_cors_rules ) ). + + " Wait for CORS configuration to propagate + WAIT UP TO 2 SECONDS. + + " Verify CORS was set by reading it back + DATA(lo_verify) = ao_s3->getbucketcors( iv_bucket = av_bucket ). + IF lo_verify IS BOUND AND lo_verify->get_corsrules( ) IS NOT INITIAL. + lv_cors_set = abap_true. + EXIT. + ENDIF. + + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_setup_ex). + " Retry on client exception + IF lv_retry = 3. + cl_abap_unit_assert=>fail( msg = |Failed to set CORS configuration after 3 attempts: { lo_setup_ex->get_text( ) }| ). + ENDIF. + WAIT UP TO 1 SECONDS. + ENDTRY. + ENDDO. - " Now test getting the CORS configuration + " Verify CORS was successfully set + cl_abap_unit_assert=>assert_true( + act = lv_cors_set + msg = |CORS configuration was not set after 3 attempts| ). + + " Now test getting the CORS configuration using the action method DATA lo_result TYPE REF TO /aws1/cl_s3_getbktcorsoutput. - ao_s3_actions->get_bucket_cors( - EXPORTING - iv_bucket_name = av_bucket - IMPORTING - oo_result = lo_result ). + TRY. + ao_s3_actions->get_bucket_cors( + EXPORTING + iv_bucket_name = av_bucket + IMPORTING + oo_result = lo_result ). - cl_abap_unit_assert=>assert_bound( - act = lo_result - msg = |Could not get bucket CORS configuration| ). + cl_abap_unit_assert=>assert_bound( + act = lo_result + msg = |Could not get bucket CORS configuration| ). + + cl_abap_unit_assert=>assert_not_initial( + act = lo_result->get_corsrules( ) + msg = |CORS rules are empty| ). + + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_ex). + cl_abap_unit_assert=>fail( msg = |CORS configuration test failed: { lo_ex->get_text( ) }| ). + ENDTRY. " Cleanup - ao_s3->deletebucketcors( iv_bucket = av_bucket ). + TRY. + ao_s3->deletebucketcors( iv_bucket = av_bucket ). + CATCH /aws1/cx_rt_generic. + " Ignore cleanup errors + ENDTRY. ENDMETHOD. METHOD put_bucket_cors. DATA lt_cors_rules TYPE /aws1/cl_s3_corsrule=>tt_corsrules. DATA lt_methods TYPE /aws1/cl_s3_allowedmethods_w=>tt_allowedmethods. DATA lt_origins TYPE /aws1/cl_s3_allowedorigins_w=>tt_allowedorigins. + DATA lv_retry TYPE i. + DATA lv_cors_set TYPE abap_bool VALUE abap_false. APPEND NEW /aws1/cl_s3_allowedmethods_w( iv_value = 'PUT' ) TO lt_methods. APPEND NEW /aws1/cl_s3_allowedmethods_w( iv_value = 'POST' ) TO lt_methods. @@ -501,18 +547,54 @@ CLASS ltc_awsex_cl_s3_actions IMPLEMENTATION. it_allowedmethods = lt_methods it_allowedorigins = lt_origins ) TO lt_cors_rules. - ao_s3_actions->put_bucket_cors( - iv_bucket_name = av_bucket - it_cors_rules = lt_cors_rules ). + " Set CORS configuration with retry logic + DO 3 TIMES. + lv_retry = sy-index. + TRY. + ao_s3_actions->put_bucket_cors( + iv_bucket_name = av_bucket + it_cors_rules = lt_cors_rules ). - " Verify CORS was set - DATA(lo_cors) = ao_s3->getbucketcors( iv_bucket = av_bucket ). - cl_abap_unit_assert=>assert_not_initial( - act = lo_cors->get_corsrules( ) - msg = |CORS configuration was not set| ). + " Wait for CORS configuration to propagate + WAIT UP TO 2 SECONDS. + + " Verify CORS was set by reading it back + DATA(lo_cors) = ao_s3->getbucketcors( iv_bucket = av_bucket ). + IF lo_cors IS BOUND AND lo_cors->get_corsrules( ) IS NOT INITIAL. + lv_cors_set = abap_true. + EXIT. + ENDIF. + + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_ex). + " Retry on client exception + IF lv_retry = 3. + cl_abap_unit_assert=>fail( msg = |Failed to set CORS configuration after 3 attempts: { lo_ex->get_text( ) }| ). + ENDIF. + WAIT UP TO 1 SECONDS. + ENDTRY. + ENDDO. + + " Verify CORS was successfully set + cl_abap_unit_assert=>assert_true( + act = lv_cors_set + msg = |CORS configuration was not set after 3 attempts| ). + + " Verify the CORS rules content + TRY. + DATA(lo_verify) = ao_s3->getbucketcors( iv_bucket = av_bucket ). + cl_abap_unit_assert=>assert_not_initial( + act = lo_verify->get_corsrules( ) + msg = |CORS configuration rules are empty| ). + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_verify_ex). + cl_abap_unit_assert=>fail( msg = |Could not verify CORS configuration: { lo_verify_ex->get_text( ) }| ). + ENDTRY. " Cleanup - ao_s3->deletebucketcors( iv_bucket = av_bucket ). + TRY. + ao_s3->deletebucketcors( iv_bucket = av_bucket ). + CATCH /aws1/cx_rt_generic. + " Ignore cleanup errors + ENDTRY. ENDMETHOD. METHOD delete_bucket_cors. @@ -679,24 +761,63 @@ CLASS ltc_awsex_cl_s3_actions IMPLEMENTATION. METHOD put_bucket_lifecycle_conf. DATA lt_rules TYPE /aws1/cl_s3_lifecyclerule=>tt_lifecyclerules. + DATA lv_retry TYPE i. + DATA lv_lifecycle_set TYPE abap_bool VALUE abap_false. + APPEND NEW /aws1/cl_s3_lifecyclerule( iv_id = 'TestRule' iv_status = 'Enabled' io_filter = NEW /aws1/cl_s3_lcrulefilter( iv_prefix = 'archive/' ) io_expiration = NEW /aws1/cl_s3_lifecycleexpir( iv_days = 60 ) ) TO lt_rules. - ao_s3_actions->put_bucket_lifecycle_conf( - iv_bucket_name = av_bucket - it_lifecycle_rule = lt_rules ). + " Set lifecycle configuration with retry logic + DO 3 TIMES. + lv_retry = sy-index. + TRY. + ao_s3_actions->put_bucket_lifecycle_conf( + iv_bucket_name = av_bucket + it_lifecycle_rule = lt_rules ). - " Verify lifecycle was set - DATA(lo_lc) = ao_s3->getbucketlifecycleconf( iv_bucket = av_bucket ). - cl_abap_unit_assert=>assert_not_initial( - act = lo_lc->get_rules( ) - msg = |Lifecycle configuration was not set| ). + " Wait for lifecycle configuration to propagate + WAIT UP TO 2 SECONDS. + + " Verify lifecycle was set by reading it back + DATA(lo_lc) = ao_s3->getbucketlifecycleconf( iv_bucket = av_bucket ). + IF lo_lc IS BOUND AND lo_lc->get_rules( ) IS NOT INITIAL. + lv_lifecycle_set = abap_true. + EXIT. + ENDIF. + + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_ex). + " Retry on client exception + IF lv_retry = 3. + cl_abap_unit_assert=>fail( msg = |Failed to set lifecycle configuration after 3 attempts: { lo_ex->get_text( ) }| ). + ENDIF. + WAIT UP TO 1 SECONDS. + ENDTRY. + ENDDO. + + " Verify lifecycle was successfully set + cl_abap_unit_assert=>assert_true( + act = lv_lifecycle_set + msg = |Lifecycle configuration was not set after 3 attempts| ). + + " Verify the rules content + TRY. + DATA(lo_verify) = ao_s3->getbucketlifecycleconf( iv_bucket = av_bucket ). + cl_abap_unit_assert=>assert_not_initial( + act = lo_verify->get_rules( ) + msg = |Lifecycle configuration rules are empty| ). + CATCH /aws1/cx_s3_clientexc INTO DATA(lo_verify_ex). + cl_abap_unit_assert=>fail( msg = |Could not verify lifecycle configuration: { lo_verify_ex->get_text( ) }| ). + ENDTRY. " Cleanup - ao_s3->deletebucketlifecycle( iv_bucket = av_bucket ). + TRY. + ao_s3->deletebucketlifecycle( iv_bucket = av_bucket ). + CATCH /aws1/cx_rt_generic. + " Ignore cleanup errors + ENDTRY. ENDMETHOD. METHOD delete_bucket_lifecycle. @@ -1103,4 +1224,30 @@ CLASS ltc_awsex_cl_s3_actions IMPLEMENTATION. ENDTRY. ENDMETHOD. + METHOD list_buckets. + DATA lo_result TYPE REF TO /aws1/cl_s3_listbucketsoutput. + + ao_s3_actions->list_buckets( + IMPORTING + oo_result = lo_result ). + + " Verify we got buckets back + cl_abap_unit_assert=>assert_bound( + act = lo_result + msg = |Could not list buckets| ). + + " Verify that our test buckets are in the list + DATA(lv_found_bucket) = abap_false. + LOOP AT lo_result->get_buckets( ) INTO DATA(lo_bucket). + IF lo_bucket->get_name( ) = av_bucket. + lv_found_bucket = abap_true. + EXIT. + ENDIF. + ENDLOOP. + + cl_abap_unit_assert=>assert_true( + act = lv_found_bucket + msg = |Test bucket { av_bucket } not found in bucket list| ). + ENDMETHOD. + ENDCLASS. diff --git a/sap-abap/services/s3/README.md b/sap-abap/services/s3/README.md index 206075339c6..e85b23b2ac9 100644 --- a/sap-abap/services/s3/README.md +++ b/sap-abap/services/s3/README.md @@ -40,35 +40,36 @@ Code examples that show you how to perform the essential operations within a ser Code excerpts that show you how to call individual service functions. -- [CopyObject](%23awsex%23cl_s3_actions.clas.abap#L194) -- [CreateBucket](%23awsex%23cl_s3_actions.clas.abap#L217) -- [DeleteBucket](%23awsex%23cl_s3_actions.clas.abap#L250) -- [DeleteBucketCors](%23awsex%23cl_s3_actions.clas.abap#L475) -- [DeleteBucketLifecycle](%23awsex%23cl_s3_actions.clas.abap#L590) -- [DeleteBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L533) -- [DeleteObject](%23awsex%23cl_s3_actions.clas.abap#L270) -- [DeleteObjects](%23awsex%23cl_s3_actions.clas.abap#L378) -- [GetBucketAcl](%23awsex%23cl_s3_actions.clas.abap#L397) -- [GetBucketCors](%23awsex%23cl_s3_actions.clas.abap#L436) -- [GetBucketLifecycleConfiguration](%23awsex%23cl_s3_actions.clas.abap#L551) -- [GetBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L493) -- [GetObject](%23awsex%23cl_s3_actions.clas.abap#L289) -- [GetObjectAcl](%23awsex%23cl_s3_actions.clas.abap#L608) -- [GetObjectLegalHold](%23awsex%23cl_s3_actions.clas.abap#L653) -- [GetObjectLockConfiguration](%23awsex%23cl_s3_actions.clas.abap#L727) -- [HeadBucket](%23awsex%23cl_s3_actions.clas.abap#L808) -- [ListObjectVersions](%23awsex%23cl_s3_actions.clas.abap#L789) -- [ListObjectsV2](%23awsex%23cl_s3_actions.clas.abap#L330) -- [PutBucketAcl](%23awsex%23cl_s3_actions.clas.abap#L415) -- [PutBucketCors](%23awsex%23cl_s3_actions.clas.abap#L454) -- [PutBucketLifecycleConfiguration](%23awsex%23cl_s3_actions.clas.abap#L569) -- [PutBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L512) -- [PutBucketVersioning](%23awsex%23cl_s3_actions.clas.abap#L767) -- [PutObject](%23awsex%23cl_s3_actions.clas.abap#L349) -- [PutObjectAcl](%23awsex%23cl_s3_actions.clas.abap#L629) -- [PutObjectLegalHold](%23awsex%23cl_s3_actions.clas.abap#L674) -- [PutObjectLockConfiguration](%23awsex%23cl_s3_actions.clas.abap#L745) -- [PutObjectRetention](%23awsex%23cl_s3_actions.clas.abap#L699) +- [CopyObject](%23awsex%23cl_s3_actions.clas.abap#L198) +- [CreateBucket](%23awsex%23cl_s3_actions.clas.abap#L221) +- [DeleteBucket](%23awsex%23cl_s3_actions.clas.abap#L254) +- [DeleteBucketCors](%23awsex%23cl_s3_actions.clas.abap#L479) +- [DeleteBucketLifecycle](%23awsex%23cl_s3_actions.clas.abap#L594) +- [DeleteBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L537) +- [DeleteObject](%23awsex%23cl_s3_actions.clas.abap#L274) +- [DeleteObjects](%23awsex%23cl_s3_actions.clas.abap#L382) +- [GetBucketAcl](%23awsex%23cl_s3_actions.clas.abap#L401) +- [GetBucketCors](%23awsex%23cl_s3_actions.clas.abap#L440) +- [GetBucketLifecycleConfiguration](%23awsex%23cl_s3_actions.clas.abap#L555) +- [GetBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L497) +- [GetObject](%23awsex%23cl_s3_actions.clas.abap#L293) +- [GetObjectAcl](%23awsex%23cl_s3_actions.clas.abap#L612) +- [GetObjectLegalHold](%23awsex%23cl_s3_actions.clas.abap#L657) +- [GetObjectLockConfiguration](%23awsex%23cl_s3_actions.clas.abap#L731) +- [HeadBucket](%23awsex%23cl_s3_actions.clas.abap#L812) +- [ListBuckets](%23awsex%23cl_s3_actions.clas.abap#L830) +- [ListObjectVersions](%23awsex%23cl_s3_actions.clas.abap#L793) +- [ListObjectsV2](%23awsex%23cl_s3_actions.clas.abap#L334) +- [PutBucketAcl](%23awsex%23cl_s3_actions.clas.abap#L419) +- [PutBucketCors](%23awsex%23cl_s3_actions.clas.abap#L458) +- [PutBucketLifecycleConfiguration](%23awsex%23cl_s3_actions.clas.abap#L573) +- [PutBucketPolicy](%23awsex%23cl_s3_actions.clas.abap#L516) +- [PutBucketVersioning](%23awsex%23cl_s3_actions.clas.abap#L771) +- [PutObject](%23awsex%23cl_s3_actions.clas.abap#L353) +- [PutObjectAcl](%23awsex%23cl_s3_actions.clas.abap#L633) +- [PutObjectLegalHold](%23awsex%23cl_s3_actions.clas.abap#L678) +- [PutObjectLockConfiguration](%23awsex%23cl_s3_actions.clas.abap#L749) +- [PutObjectRetention](%23awsex%23cl_s3_actions.clas.abap#L703) ### Scenarios