diff --git a/.doc_gen/metadata/iam_metadata.yaml b/.doc_gen/metadata/iam_metadata.yaml index 0bda611f2a6..d93258f88e9 100644 --- a/.doc_gen/metadata/iam_metadata.yaml +++ b/.doc_gen/metadata/iam_metadata.yaml @@ -1487,6 +1487,14 @@ iam_GetPolicyVersion: - description: snippet_tags: - python.example_code.iam.GetPolicyVersion + SAP ABAP: + versions: + - sdk_version: 1 + github: sap-abap/services/iam + excerpts: + - description: + snippet_tags: + - iam.abapv1.get_policy_version services: iam: {GetPolicyVersion} iam_Scenario_RollbackPolicyVersion: diff --git a/.doc_gen/metadata/iot-data_metadata.yaml b/.doc_gen/metadata/iot-data_metadata.yaml index f48bdfcfd29..98a1eda3da1 100644 --- a/.doc_gen/metadata/iot-data_metadata.yaml +++ b/.doc_gen/metadata/iot-data_metadata.yaml @@ -43,6 +43,14 @@ iot-data-plane_GetThingShadow: - description: snippet_tags: - cpp.example_code.iot.GetThingShadow + SAP ABAP: + versions: + - sdk_version: 1 + github: sap-abap/services/iop + excerpts: + - description: + snippet_tags: + - iop.abapv1.get_thing_shadow services: iot-data-plane: {GetThingShadow} iot-data-plane_UpdateThingShadow: @@ -89,5 +97,13 @@ iot-data-plane_UpdateThingShadow: - description: snippet_tags: - cpp.example_code.iot.UpdateThingShadow + SAP ABAP: + versions: + - sdk_version: 1 + github: sap-abap/services/iop + excerpts: + - description: + snippet_tags: + - iop.abapv1.update_thing_shadow services: iot-data-plane: {UpdateThingShadow} diff --git a/.tools/readmes/config.py b/.tools/readmes/config.py index 0d8733eddb4..0dd797bf232 100644 --- a/.tools/readmes/config.py +++ b/.tools/readmes/config.py @@ -211,6 +211,7 @@ "firehose": "sap-abap/services/frh", "glue": "sap-abap/services/glu", "healthlake": "sap-abap/services/hll", + "iot-data-plane": "sap-abap/services/iop", "iotsitewise": "sap-abap/services/ios", "keyspaces": "sap-abap/services/kys", "kinesis": "sap-abap/services/kns", diff --git a/sap-abap/services/iam/#awsex#cl_iam_actions.clas.abap b/sap-abap/services/iam/#awsex#cl_iam_actions.clas.abap index 18a00a39c34..3a34d67c9f2 100644 --- a/sap-abap/services/iam/#awsex#cl_iam_actions.clas.abap +++ b/sap-abap/services/iam/#awsex#cl_iam_actions.clas.abap @@ -280,6 +280,15 @@ CLASS /awsex/cl_iam_actions DEFINITION RAISING /aws1/cx_rt_generic. + METHODS get_policy_version + IMPORTING + !iv_policy_arn TYPE /aws1/iamarntype + !iv_version_id TYPE /aws1/iampolicyversionidtype + EXPORTING + !oo_result TYPE REF TO /aws1/cl_iamgetpolicyvrsrsp + RAISING + /aws1/cx_rt_generic. + PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. @@ -1027,4 +1036,25 @@ CLASS /AWSEX/CL_IAM_ACTIONS IMPLEMENTATION. ENDTRY. " snippet-end:[iam.abapv1.delete_policy_version] ENDMETHOD. + + + METHOD get_policy_version. + CONSTANTS cv_pfl TYPE /aws1/rt_profile_id VALUE 'ZCODE_DEMO'. + + DATA(lo_session) = /aws1/cl_rt_session_aws=>create( cv_pfl ). + DATA(lo_iam) = /aws1/cl_iam_factory=>create( lo_session ). + + " snippet-start:[iam.abapv1.get_policy_version] + TRY. + oo_result = lo_iam->getpolicyversion( + iv_policyarn = iv_policy_arn + iv_versionid = iv_version_id ). + MESSAGE 'Retrieved policy version information.' TYPE 'I'. + CATCH /aws1/cx_iamnosuchentityex. + MESSAGE 'Policy or version does not exist.' TYPE 'E'. + CATCH /aws1/cx_iaminvalidinputex. + MESSAGE 'Invalid input provided.' TYPE 'E'. + ENDTRY. + " snippet-end:[iam.abapv1.get_policy_version] + ENDMETHOD. ENDCLASS. diff --git a/sap-abap/services/iam/#awsex#cl_iam_actions.clas.testclasses.abap b/sap-abap/services/iam/#awsex#cl_iam_actions.clas.testclasses.abap index 7b616c0c083..43d1f5c43f5 100644 --- a/sap-abap/services/iam/#awsex#cl_iam_actions.clas.testclasses.abap +++ b/sap-abap/services/iam/#awsex#cl_iam_actions.clas.testclasses.abap @@ -67,6 +67,7 @@ CLASS ltc_awsex_cl_iam_actions DEFINITION FOR TESTING DURATION LONG RISK LEVEL D list_saml_providers FOR TESTING RAISING /aws1/cx_rt_generic, create_service_linked_role FOR TESTING RAISING /aws1/cx_rt_generic, list_policy_versions FOR TESTING RAISING /aws1/cx_rt_generic, + get_policy_version FOR TESTING RAISING /aws1/cx_rt_generic, set_default_policy_version FOR TESTING RAISING /aws1/cx_rt_generic, delete_policy_version FOR TESTING RAISING /aws1/cx_rt_generic. @@ -1507,6 +1508,56 @@ CLASS ltc_awsex_cl_iam_actions IMPLEMENTATION. msg = |Policy should have a default version| ). ENDMETHOD. + + METHOD get_policy_version. + DATA lo_result TYPE REF TO /aws1/cl_iamgetpolicyvrsrsp. + DATA lv_version_id TYPE /aws1/iampolicyversionidtype. + + " Use test policy created in class_setup + " First get the list of versions to find the default version ID + DATA(lo_list_result) = ao_iam->listpolicyversions( iv_policyarn = av_test_policy_arn ). + DATA(lt_versions) = lo_list_result->get_versions( ). + + " Find the default version + LOOP AT lt_versions INTO DATA(lo_version). + IF lo_version->get_isdefaultversion( ) = abap_true. + lv_version_id = lo_version->get_versionid( ). + EXIT. + ENDIF. + ENDLOOP. + + cl_abap_unit_assert=>assert_not_initial( + act = lv_version_id + msg = |Default policy version ID should be found| ). + + " Now get the policy version details + ao_iam_actions->get_policy_version( + EXPORTING + iv_policy_arn = av_test_policy_arn + iv_version_id = lv_version_id + IMPORTING + oo_result = lo_result ). + + cl_abap_unit_assert=>assert_bound( + act = lo_result + msg = |Get policy version result should not be initial| ). + + DATA(lo_policy_version) = lo_result->get_policyversion( ). + cl_abap_unit_assert=>assert_bound( + act = lo_policy_version + msg = |Policy version should not be initial| ). + + cl_abap_unit_assert=>assert_equals( + exp = lv_version_id + act = lo_policy_version->get_versionid( ) + msg = |Version ID should match| ). + + cl_abap_unit_assert=>assert_not_initial( + act = lo_policy_version->get_document( ) + msg = |Policy document should not be initial| ). + ENDMETHOD. + + METHOD set_default_policy_version. DATA lv_new_policy_doc TYPE string. DATA lv_version_id TYPE /aws1/iampolicyversionidtype. diff --git a/sap-abap/services/iam/README.md b/sap-abap/services/iam/README.md index a5ce4acae6b..7f6b58ed71a 100644 --- a/sap-abap/services/iam/README.md +++ b/sap-abap/services/iam/README.md @@ -33,44 +33,45 @@ For prerequisites, see the [README](../../README.md#Prerequisites) in the `sap-a Code excerpts that show you how to call individual service functions. -- [AttachRolePolicy](%23awsex%23cl_iam_actions.clas.abap#L689) -- [AttachUserPolicy](%23awsex%23cl_iam_actions.clas.abap#L573) -- [CreateAccessKey](%23awsex%23cl_iam_actions.clas.abap#L377) -- [CreateAccountAlias](%23awsex%23cl_iam_actions.clas.abap#L782) -- [CreatePolicy](%23awsex%23cl_iam_actions.clas.abap#L472) -- [CreatePolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L549) -- [CreateRole](%23awsex%23cl_iam_actions.clas.abap#L613) -- [CreateServiceLinkedRole](%23awsex%23cl_iam_actions.clas.abap#L972) -- [CreateUser](%23awsex%23cl_iam_actions.clas.abap#L298) -- [DeleteAccessKey](%23awsex%23cl_iam_actions.clas.abap#L397) -- [DeleteAccountAlias](%23awsex%23cl_iam_actions.clas.abap#L802) -- [DeletePolicy](%23awsex%23cl_iam_actions.clas.abap#L496) -- [DeletePolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L1015) -- [DeleteRole](%23awsex%23cl_iam_actions.clas.abap#L636) -- [DeleteUser](%23awsex%23cl_iam_actions.clas.abap#L320) -- [DetachRolePolicy](%23awsex%23cl_iam_actions.clas.abap#L710) -- [DetachUserPolicy](%23awsex%23cl_iam_actions.clas.abap#L594) -- [GenerateCredentialReport](%23awsex%23cl_iam_actions.clas.abap#L871) -- [GetAccessKeyLastUsed](%23awsex%23cl_iam_actions.clas.abap#L454) -- [GetAccountAuthorizationDetails](%23awsex%23cl_iam_actions.clas.abap#L837) -- [GetAccountPasswordPolicy](%23awsex%23cl_iam_actions.clas.abap#L913) -- [GetAccountSummary](%23awsex%23cl_iam_actions.clas.abap#L854) -- [GetCredentialReport](%23awsex%23cl_iam_actions.clas.abap#L890) -- [GetPolicy](%23awsex%23cl_iam_actions.clas.abap#L532) -- [GetRole](%23awsex%23cl_iam_actions.clas.abap#L655) -- [ListAccessKeys](%23awsex%23cl_iam_actions.clas.abap#L416) -- [ListAccountAliases](%23awsex%23cl_iam_actions.clas.abap#L820) -- [ListAttachedRolePolicies](%23awsex%23cl_iam_actions.clas.abap#L729) -- [ListGroups](%23awsex%23cl_iam_actions.clas.abap#L765) -- [ListPolicies](%23awsex%23cl_iam_actions.clas.abap#L515) -- [ListPolicyVersions](%23awsex%23cl_iam_actions.clas.abap#L972) -- [ListRolePolicies](%23awsex%23cl_iam_actions.clas.abap#L747) -- [ListRoles](%23awsex%23cl_iam_actions.clas.abap#L672) -- [ListSAMLProviders](%23awsex%23cl_iam_actions.clas.abap#L932) -- [ListUsers](%23awsex%23cl_iam_actions.clas.abap#L339) -- [SetDefaultPolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L992) -- [UpdateAccessKey](%23awsex%23cl_iam_actions.clas.abap#L434) -- [UpdateUser](%23awsex%23cl_iam_actions.clas.abap#L356) +- [AttachRolePolicy](%23awsex%23cl_iam_actions.clas.abap#L698) +- [AttachUserPolicy](%23awsex%23cl_iam_actions.clas.abap#L582) +- [CreateAccessKey](%23awsex%23cl_iam_actions.clas.abap#L386) +- [CreateAccountAlias](%23awsex%23cl_iam_actions.clas.abap#L791) +- [CreatePolicy](%23awsex%23cl_iam_actions.clas.abap#L481) +- [CreatePolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L558) +- [CreateRole](%23awsex%23cl_iam_actions.clas.abap#L622) +- [CreateServiceLinkedRole](%23awsex%23cl_iam_actions.clas.abap#L981) +- [CreateUser](%23awsex%23cl_iam_actions.clas.abap#L307) +- [DeleteAccessKey](%23awsex%23cl_iam_actions.clas.abap#L406) +- [DeleteAccountAlias](%23awsex%23cl_iam_actions.clas.abap#L811) +- [DeletePolicy](%23awsex%23cl_iam_actions.clas.abap#L505) +- [DeletePolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L1024) +- [DeleteRole](%23awsex%23cl_iam_actions.clas.abap#L645) +- [DeleteUser](%23awsex%23cl_iam_actions.clas.abap#L329) +- [DetachRolePolicy](%23awsex%23cl_iam_actions.clas.abap#L719) +- [DetachUserPolicy](%23awsex%23cl_iam_actions.clas.abap#L603) +- [GenerateCredentialReport](%23awsex%23cl_iam_actions.clas.abap#L880) +- [GetAccessKeyLastUsed](%23awsex%23cl_iam_actions.clas.abap#L463) +- [GetAccountAuthorizationDetails](%23awsex%23cl_iam_actions.clas.abap#L846) +- [GetAccountPasswordPolicy](%23awsex%23cl_iam_actions.clas.abap#L922) +- [GetAccountSummary](%23awsex%23cl_iam_actions.clas.abap#L863) +- [GetCredentialReport](%23awsex%23cl_iam_actions.clas.abap#L899) +- [GetPolicy](%23awsex%23cl_iam_actions.clas.abap#L541) +- [GetPolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L1047) +- [GetRole](%23awsex%23cl_iam_actions.clas.abap#L664) +- [ListAccessKeys](%23awsex%23cl_iam_actions.clas.abap#L425) +- [ListAccountAliases](%23awsex%23cl_iam_actions.clas.abap#L829) +- [ListAttachedRolePolicies](%23awsex%23cl_iam_actions.clas.abap#L738) +- [ListGroups](%23awsex%23cl_iam_actions.clas.abap#L774) +- [ListPolicies](%23awsex%23cl_iam_actions.clas.abap#L524) +- [ListPolicyVersions](%23awsex%23cl_iam_actions.clas.abap#L981) +- [ListRolePolicies](%23awsex%23cl_iam_actions.clas.abap#L756) +- [ListRoles](%23awsex%23cl_iam_actions.clas.abap#L681) +- [ListSAMLProviders](%23awsex%23cl_iam_actions.clas.abap#L941) +- [ListUsers](%23awsex%23cl_iam_actions.clas.abap#L348) +- [SetDefaultPolicyVersion](%23awsex%23cl_iam_actions.clas.abap#L1001) +- [UpdateAccessKey](%23awsex%23cl_iam_actions.clas.abap#L443) +- [UpdateUser](%23awsex%23cl_iam_actions.clas.abap#L365) diff --git a/sap-abap/services/iop/#awsex#cl_iop_actions.clas.abap b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.abap new file mode 100644 index 00000000000..a06058bcba0 --- /dev/null +++ b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.abap @@ -0,0 +1,87 @@ +" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +" SPDX-License-Identifier: Apache-2.0 +CLASS /awsex/cl_iop_actions DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + " Updates the shadow for an AWS IoT thing + " @parameter iv_thing_name | The name of the thing (e.g., 'MyIoTDevice') + " @parameter iv_shadow_state | The shadow state as JSON string + " @raising /aws1/cx_rt_generic | Thrown when operation fails + METHODS update_thing_shadow + IMPORTING + !iv_thing_name TYPE /aws1/iopthingname + !iv_shadow_state TYPE string + RAISING + /aws1/cx_rt_generic. + + " Gets the shadow for an AWS IoT thing + " @parameter iv_thing_name | The name of the thing (e.g., 'MyIoTDevice') + " @parameter ov_shadow | The shadow state as JSON string + " @raising /aws1/cx_rt_generic | Thrown when operation fails + METHODS get_thing_shadow + IMPORTING + !iv_thing_name TYPE /aws1/iopthingname + RETURNING + VALUE(ov_shadow) TYPE string + RAISING + /aws1/cx_rt_generic. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS /awsex/cl_iop_actions IMPLEMENTATION. + + METHOD update_thing_shadow. + CONSTANTS cv_pfl TYPE /aws1/rt_profile_id VALUE 'ZCODE_DEMO'. + + DATA(lo_session) = /aws1/cl_rt_session_aws=>create( cv_pfl ). + DATA(lo_iop) = /aws1/cl_iop_factory=>create( lo_session ). + + " snippet-start:[iop.abapv1.update_thing_shadow] + TRY. + " Convert JSON string to xstring for payload + DATA(lv_payload) = /aws1/cl_rt_util=>string_to_xstring( iv_shadow_state ). + + lo_iop->updatethingshadow( + iv_thingname = iv_thing_name + iv_payload = lv_payload ). + MESSAGE 'Thing shadow updated successfully.' TYPE 'I'. + CATCH /aws1/cx_iopresourcenotfoundex. + MESSAGE 'Thing not found.' TYPE 'E'. + CATCH /aws1/cx_rt_generic INTO DATA(lo_exception). + DATA(lv_error) = |{ lo_exception->get_text( ) }|. + MESSAGE lv_error TYPE 'E'. + ENDTRY. + " snippet-end:[iop.abapv1.update_thing_shadow] + ENDMETHOD. + + METHOD get_thing_shadow. + CONSTANTS cv_pfl TYPE /aws1/rt_profile_id VALUE 'ZCODE_DEMO'. + + DATA(lo_session) = /aws1/cl_rt_session_aws=>create( cv_pfl ). + DATA(lo_iop) = /aws1/cl_iop_factory=>create( lo_session ). + + " snippet-start:[iop.abapv1.get_thing_shadow] + TRY. + DATA(lo_result) = lo_iop->getthingshadow( iv_thingname = iv_thing_name ). + + " Convert xstring payload to JSON string + DATA(lv_payload) = lo_result->get_payload( ). + ov_shadow = /aws1/cl_rt_util=>xstring_to_string( lv_payload ). + MESSAGE 'Thing shadow retrieved successfully.' TYPE 'I'. + CATCH /aws1/cx_iopresourcenotfoundex. + MESSAGE 'Thing shadow not found.' TYPE 'E'. + CATCH /aws1/cx_rt_generic INTO DATA(lo_exception). + DATA(lv_error) = |{ lo_exception->get_text( ) }|. + MESSAGE lv_error TYPE 'E'. + ENDTRY. + " snippet-end:[iop.abapv1.get_thing_shadow] + ENDMETHOD. + +ENDCLASS. diff --git a/sap-abap/services/iop/#awsex#cl_iop_actions.clas.testclasses.abap b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.testclasses.abap new file mode 100644 index 00000000000..afa22941dd5 --- /dev/null +++ b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.testclasses.abap @@ -0,0 +1,289 @@ +" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +" SPDX-License-Identifier: Apache-2.0 +CLASS ltc_awsex_cl_iop_actions DEFINITION DEFERRED. +CLASS /awsex/cl_iop_actions DEFINITION LOCAL FRIENDS ltc_awsex_cl_iop_actions. + +CLASS ltc_awsex_cl_iop_actions DEFINITION FOR TESTING DURATION LONG RISK LEVEL DANGEROUS. + + PRIVATE SECTION. + CONSTANTS cv_pfl TYPE /aws1/rt_profile_id VALUE 'ZCODE_DEMO'. + + CLASS-DATA ao_iot TYPE REF TO /aws1/if_iot. + CLASS-DATA ao_iop TYPE REF TO /aws1/if_iop. + CLASS-DATA ao_session TYPE REF TO /aws1/cl_rt_session_base. + CLASS-DATA ao_iop_actions TYPE REF TO /awsex/cl_iop_actions. + + CLASS-DATA av_thing_name TYPE /aws1/iopthingname. + + METHODS: update_thing_shadow FOR TESTING RAISING /aws1/cx_rt_generic, + get_thing_shadow FOR TESTING RAISING /aws1/cx_rt_generic. + + CLASS-METHODS class_setup RAISING /aws1/cx_rt_generic. + CLASS-METHODS class_teardown RAISING /aws1/cx_rt_generic. + + CLASS-METHODS wait_for_thing_ready + IMPORTING + iv_thing_name TYPE /aws1/iotthingname + iv_max_wait TYPE i DEFAULT 30 + RAISING + /aws1/cx_rt_generic. + +ENDCLASS. + +CLASS ltc_awsex_cl_iop_actions IMPLEMENTATION. + + METHOD class_setup. + ao_session = /aws1/cl_rt_session_aws=>create( cv_pfl ). + ao_iot = /aws1/cl_iot_factory=>create( ao_session ). + ao_iop = /aws1/cl_iop_factory=>create( ao_session ). + ao_iop_actions = NEW /awsex/cl_iop_actions( ). + + " Create a test thing for shadow operations - MUST succeed + DATA(lv_uuid) = /awsex/cl_utils=>get_random_string( ). + av_thing_name = |sap-abap-iot-shadow-{ lv_uuid }|. + + TRY. + DATA(lo_thing_result) = ao_iot->creatething( iv_thingname = av_thing_name ). + + IF lo_thing_result IS NOT BOUND. + MESSAGE 'Failed to create IoT thing for shadow tests' TYPE 'E'. + ENDIF. + + " Verify thing was created + cl_abap_unit_assert=>assert_not_initial( + act = lo_thing_result->get_thingname( ) + msg = |Thing name is initial after creation| ). + + CATCH /aws1/cx_iotresrcalrdyexistsex. + " Thing already exists - verify it's accessible + TRY. + ao_iot->describething( iv_thingname = av_thing_name ). + CATCH /aws1/cx_rt_generic INTO DATA(lo_desc_ex). + MESSAGE |Thing exists but cannot be accessed: { lo_desc_ex->get_text( ) }| TYPE 'E'. + ENDTRY. + CATCH /aws1/cx_rt_generic INTO DATA(lo_create_ex). + MESSAGE |Failed to create thing: { lo_create_ex->get_text( ) }| TYPE 'E'. + ENDTRY. + + " Wait for thing to be fully ready + wait_for_thing_ready( iv_thing_name = av_thing_name ). + + ENDMETHOD. + + METHOD class_teardown. + " Clean up test thing + IF av_thing_name IS NOT INITIAL. + TRY. + " Delete the thing shadow first if it exists + TRY. + ao_iop->deletethingshadow( iv_thingname = av_thing_name ). + " Wait for shadow deletion to propagate + WAIT UP TO 2 SECONDS. + CATCH /aws1/cx_rt_generic. + " Shadow may not exist + ENDTRY. + + " Delete the thing + ao_iot->deletething( iv_thingname = av_thing_name ). + CATCH /aws1/cx_rt_generic. + " Thing may not exist or may have dependencies + ENDTRY. + ENDIF. + + ENDMETHOD. + + METHOD wait_for_thing_ready. + " Wait for thing to be ready with status-based polling + DATA lv_elapsed TYPE i VALUE 0. + DATA lv_ready TYPE abap_bool VALUE abap_false. + DATA lv_start_time TYPE timestampl. + DATA lv_current_time TYPE timestampl. + + GET TIME STAMP FIELD lv_start_time. + + WHILE lv_elapsed < iv_max_wait AND lv_ready = abap_false. + TRY. + " Try to describe the thing + DATA(lo_thing_desc) = ao_iot->describething( iv_thingname = iv_thing_name ). + IF lo_thing_desc IS BOUND. + lv_ready = abap_true. + ELSE. + WAIT UP TO 2 SECONDS. + GET TIME STAMP FIELD lv_current_time. + lv_elapsed = lv_current_time - lv_start_time. + ENDIF. + + CATCH /aws1/cx_rt_generic. + " Thing not ready yet + WAIT UP TO 2 SECONDS. + GET TIME STAMP FIELD lv_current_time. + lv_elapsed = lv_current_time - lv_start_time. + ENDTRY. + ENDWHILE. + + IF lv_ready = abap_false. + MESSAGE 'Thing did not become ready in time' TYPE 'E'. + ENDIF. + + ENDMETHOD. + + METHOD update_thing_shadow. + " Verify thing exists + TRY. + ao_iot->describething( iv_thingname = av_thing_name ). + CATCH /aws1/cx_rt_generic INTO DATA(lo_verify_ex). + MESSAGE |Thing does not exist: { lo_verify_ex->get_text( ) }| TYPE 'E'. + ENDTRY. + + " Create a shadow state JSON + DATA lv_shadow_state TYPE string. + lv_shadow_state = + '{"state":{"desired":{"temperature":25,"humidity":60},"reported":{"temperature":24,"humidity":58}}}'. + + " Update thing shadow - this MUST succeed + ao_iop_actions->update_thing_shadow( + iv_thing_name = av_thing_name + iv_shadow_state = lv_shadow_state ). + + " Wait for shadow to be updated + WAIT UP TO 3 SECONDS. + + " Verify shadow was updated by getting it - MUST be retrievable + DATA(lo_shadow_result) = ao_iop->getthingshadow( iv_thingname = av_thing_name ). + + cl_abap_unit_assert=>assert_bound( + act = lo_shadow_result + msg = |Shadow result is not bound| ). + + DATA(lv_payload) = lo_shadow_result->get_payload( ). + + cl_abap_unit_assert=>assert_not_initial( + act = lv_payload + msg = |Shadow payload is empty| ). + + " Convert payload to string and verify it contains expected data + DATA(lv_shadow_json) = /aws1/cl_rt_util=>xstring_to_string( lv_payload ). + + cl_abap_unit_assert=>assert_not_initial( + act = lv_shadow_json + msg = |Shadow JSON is empty| ). + + " Verify shadow structure - MUST contain expected fields + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow_json sub = 'state' ) + exp = -1 + msg = |Shadow does not contain state field| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow_json sub = 'desired' ) + exp = -1 + msg = |Shadow does not contain desired state| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow_json sub = 'reported' ) + exp = -1 + msg = |Shadow does not contain reported state| ). + + " Verify our test values are present + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow_json sub = 'temperature' ) + exp = -1 + msg = |Shadow does not contain temperature field| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow_json sub = 'humidity' ) + exp = -1 + msg = |Shadow does not contain humidity field| ). + + ENDMETHOD. + + METHOD get_thing_shadow. + " Verify thing exists + TRY. + ao_iot->describething( iv_thingname = av_thing_name ). + CATCH /aws1/cx_rt_generic INTO DATA(lo_verify_ex). + MESSAGE |Thing does not exist: { lo_verify_ex->get_text( ) }| TYPE 'E'. + ENDTRY. + + " First ensure shadow exists by updating it + DATA lv_shadow_state TYPE string. + lv_shadow_state = + '{"state":{"desired":{"power":"on","level":75},"reported":{"power":"on","level":70}}}'. + + TRY. + ao_iop->updatethingshadow( + iv_thingname = av_thing_name + iv_payload = /aws1/cl_rt_util=>string_to_xstring( lv_shadow_state ) ). + CATCH /aws1/cx_rt_generic INTO DATA(lo_update_ex). + MESSAGE |Failed to create shadow for test: { lo_update_ex->get_text( ) }| TYPE 'E'. + ENDTRY. + + " Wait for shadow to be updated with status-based polling + DATA lv_shadow_ready TYPE abap_bool VALUE abap_false. + DATA lv_retry_count TYPE i VALUE 0. + + WHILE lv_retry_count < 10 AND lv_shadow_ready = abap_false. + TRY. + DATA(lo_check_shadow) = ao_iop->getthingshadow( iv_thingname = av_thing_name ). + IF lo_check_shadow IS BOUND AND lo_check_shadow->get_payload( ) IS NOT INITIAL. + lv_shadow_ready = abap_true. + ELSE. + lv_retry_count = lv_retry_count + 1. + WAIT UP TO 2 SECONDS. + ENDIF. + CATCH /aws1/cx_rt_generic. + lv_retry_count = lv_retry_count + 1. + IF lv_retry_count < 10. + WAIT UP TO 2 SECONDS. + ELSE. + MESSAGE 'Shadow did not become ready in time' TYPE 'E'. + ENDIF. + ENDTRY. + ENDWHILE. + + IF lv_shadow_ready = abap_false. + MESSAGE 'Shadow was not created successfully' TYPE 'E'. + ENDIF. + + " Now get the shadow using our action method - this MUST succeed + DATA(lv_shadow) = ao_iop_actions->get_thing_shadow( iv_thing_name = av_thing_name ). + + cl_abap_unit_assert=>assert_not_initial( + act = lv_shadow + msg = |Shadow is empty| ). + + " Verify shadow contains expected structure - MUST contain these fields + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow sub = 'state' ) + exp = -1 + msg = |Shadow does not contain state| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow sub = 'desired' ) + exp = -1 + msg = |Shadow does not contain desired state| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow sub = 'reported' ) + exp = -1 + msg = |Shadow does not contain reported state| ). + + " Verify it contains our test values - MUST contain these fields + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow sub = 'power' ) + exp = -1 + msg = |Shadow does not contain power field| ). + + cl_abap_unit_assert=>assert_differs( + act = find( val = lv_shadow sub = 'level' ) + exp = -1 + msg = |Shadow does not contain level field| ). + + " Verify the actual values are correct + cl_abap_unit_assert=>assert_true( + act = xsdbool( find( val = lv_shadow sub = '75' ) >= 0 OR find( val = lv_shadow sub = '"level":75' ) >= 0 ) + msg = |Shadow does not contain expected level value 75| ). + + ENDMETHOD. + +ENDCLASS. diff --git a/sap-abap/services/iop/#awsex#cl_iop_actions.clas.xml b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.xml new file mode 100644 index 00000000000..e19f0a776f3 --- /dev/null +++ b/sap-abap/services/iop/#awsex#cl_iop_actions.clas.xml @@ -0,0 +1,17 @@ + + + + + + /AWSEX/CL_IOP_ACTIONS + E + Iot-data-plane Code Example + 1 + X + X + X + X + + + + \ No newline at end of file diff --git a/sap-abap/services/iop/README.md b/sap-abap/services/iop/README.md new file mode 100644 index 00000000000..832a014be88 --- /dev/null +++ b/sap-abap/services/iop/README.md @@ -0,0 +1,79 @@ +# AWS IoT data code examples for the SDK for SAP ABAP + +## Overview + +Shows how to use the AWS SDK for SAP ABAP to work with AWS IoT data. + + + + +_AWS IoT data _ + +## ⚠ Important + +* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/). +* Running the tests might result in charges to your AWS account. +* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege). +* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services). + + + + +## Code examples + +### Prerequisites + +For prerequisites, see the [README](../../README.md#Prerequisites) in the `sap-abap` folder. + + + + + +### Single actions + +Code excerpts that show you how to call individual service functions. + +- [GetThingShadow](%23awsex%23cl_iop_actions.clas.abap#L70) +- [UpdateThingShadow](%23awsex%23cl_iop_actions.clas.abap#L46) + + + + + +## Run the examples + +### Instructions + + + + + + + +### Tests + +⚠ Running tests might result in charges to your AWS account. + + +To find instructions for running these tests, see the [README](../../README.md#Tests) +in the `sap-abap` folder. + + + + + + +## Additional resources + +- [AWS IoT data Developer Guide](https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html) +- [AWS IoT data API Reference](https://docs.aws.amazon.com/iot/latest/apireference/Welcome.html) +- [SDK for SAP ABAP AWS IoT data reference](https://docs.aws.amazon.com/sdk-for-sap-abap/v1/api/latest/iot-data-plane/index.html) + + + + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 diff --git a/sap-abap/services/iop/package.devc.xml b/sap-abap/services/iop/package.devc.xml new file mode 100644 index 00000000000..2b097a9ba19 --- /dev/null +++ b/sap-abap/services/iop/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + Package for Iot data plane + + + + \ No newline at end of file