This is a test message to test the length of the message box.
Login
ABAP RAP Unmanaged
Erstellt von Software-Heroes

RAP - Unmanaged (Remote)

150

Hier erfährst du mehr über das Unmanaged RAP Objekt mit einer Remote Datenquelle und wie du solche Szenarien umsetzen kannst.

Werbung


Im ersten Artikel der Reihe haben wir vor allem über die Theorie gesprochen und im zweiten Artikel ein lokales Szenario dargestellt. In diesem Artikel schauen wir uns einmal eine Applikation an, die auf Remote Daten basiert.

 

Einleitung

Wie sieht es also aus, wenn die Daten nicht im System sind, wie kommen wir an diese heran und wie können wir dazu eine lauffähige Anwendung implementieren? Das gezeigte Beispiel wird dazu eine Custom Entity verwenden und diese mit dem Unmanaged Ansatz verbinden. Als Grundlage verwenden wir den OData Service, den wir bereits in diesem Artikel angebunden und verwendet haben. Dieser unterstützt alle CRUDQ Operationen und kann damit auch leicht in unsere Anwendung implementiert werden.

 

Custom Entity

Der erste Schritt ist die Anlage der Custom Entity, hierzu können wir als Vorlage den abstrakten CDS View nehmen, der durch die Schnittstelle im System angelegt wurde. Die Custom Entity definiert die Felder und die Datentypen in einer Entität. Da wir hier mit RAP arbeiten wollen, benötigen wir weiterhin eine ROOT Entität, diese können wir ebenfalls schon im View definieren. Da wir auch Daten zur Verfügung stellen wollen, müssen wir auch eine Query Klasse zur Verfügung stellen. Nach der Anlage der Entität erhalten wir das folgende Ergebnis:

@EndUserText.label: 'Custom Entity with Unmanaged'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_CUSTOM_COMPANY_QRY'
define root custom entity ZBS_R_RAPCustomCompanyNames
{

  key CompanyName        : abap.char( 60 );
      Branch             : abap.char( 50 );
      CompanyDescription : abap.char( 255 );

}

 

Da wir eine UI Anwendung definieren wollen, müssen wir noch entsprechende UI Annotationen anlegen, um die Oberfläche auszuprägen. Nach Anlage der Annotationen erhalten wir nun die vollständige Custom Entity: 

@EndUserText.label: 'Custom Entity with Unmanaged'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_CUSTOM_COMPANY_QRY'
@UI: {
  headerInfo: {
    typeName: 'Company name',
    typeNamePlural: 'Company names',
    title: { value: 'CompanyName' }
  }
}
define root custom entity ZBS_R_RAPCustomCompanyNames
{
      @UI.facet          : [
        {
          id             : 'FacetDetailPage',
          label          : 'General',
          type           : #IDENTIFICATION_REFERENCE,
          targetQualifier: 'DETAIL'
        }
      ]

      @UI                : {
        lineItem         : [{ position: 10 }],
        selectionField   : [ { position: 10 } ],
        identification   : [{ position: 10, qualifier: 'DETAIL' }]
      }
      @EndUserText.label: 'Company name'
  key CompanyName        : abap.char( 60 );
      @UI                : {
        lineItem         : [{ position: 20 }],
        identification   : [{ position: 20, qualifier: 'DETAIL' }]
      }
      @EndUserText.label: 'Branch'
      Branch             : abap.char( 50 );
      @UI                : {
        identification   : [{ position: 30, qualifier: 'DETAIL' }]
      }
      @EndUserText.label: 'Description'
      @UI.multiLineText: true
      CompanyDescription : abap.char( 255 );

}

 

Hinweis: Verwenden wir eine Custom Entity als Startpunkt unserer Anwendung, können wir keine Datenmodellierung durchführen und somit keine Projektion anlegen. Auch wird für diese Art Entität keine Metadata Extension angeboten, sodass wir alle benötigten Komponenten in die View packen müssen.

 

Weitere Objekte

Um nun eine ausführbare Anwendung zu erhalten, müssen wir noch die restlichen Objekte implementieren. Dazu legen wir die Query Klasse an, die später unsere Abfragen Richtung Backend weiterleiten soll. Da wir ein Feld als Suchfeld definiert haben, sollten wir auch eine entsprechende Abfragelogik implementieren. Wie bereits im Artikel über die On-Premise Anbindung verwenden wir eine entsprechende Destination, rufen den Proxy auf und wandeln die Anfrage entsprechend um. Die fertige Klasse nun im Überblick:

CLASS zcl_bs_demo_custom_company_qry DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_rap_query_provider.

    CONSTANTS c_destination TYPE string                                         VALUE `<destination-service-id>`.
    CONSTANTS c_entity      TYPE /iwbep/if_cp_runtime_types=>ty_entity_set_name VALUE 'COMPANYNAMES'.

    CLASS-METHODS get_proxy
      RETURNING VALUE(ro_result) TYPE REF TO /iwbep/if_cp_client_proxy.

  PRIVATE SECTION.
    TYPES tt_result TYPE STANDARD TABLE OF ZBS_I_RAPCustomEntityCNames WITH EMPTY KEY.

    METHODS read_data_by_request
      IMPORTING io_request TYPE REF TO if_rap_query_request
      EXPORTING et_result  TYPE tt_result
                ed_count   TYPE int8.
ENDCLASS.


CLASS zcl_bs_demo_custom_company_qry IMPLEMENTATION.
  METHOD if_rap_query_provider~select.
    DATA lt_result TYPE STANDARD TABLE OF ZBS_R_RAPCustomCompanyNames.

    read_data_by_request( EXPORTING io_request = io_request
                          IMPORTING et_result  = DATA(lt_company_names)
                                    ed_count   = DATA(ld_count) ).

    IF io_request->is_total_numb_of_rec_requested( ).
      io_response->set_total_number_of_records( ld_count ).
    ENDIF.

    IF io_request->is_data_requested( ).
      lt_result = CORRESPONDING #( lt_company_names ).
      io_response->set_data( lt_result ).
    ENDIF.
  ENDMETHOD.


  METHOD get_proxy.
    TRY.
        DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination(
            i_name       = c_destination
            i_authn_mode = if_a4c_cp_service=>service_specific ).

        DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_destination ).

        ro_result = cl_web_odata_client_factory=>create_v2_remote_proxy(
            iv_service_definition_name = 'ZBS_DEMO_RAP_ONPREM_ODATA'
            io_http_client             = lo_client
            iv_relative_service_root   = '/sap/opu/odata/sap/ZBS_API_COMPANY_NAMES_O2' ).

      CATCH cx_root.
    ENDTRY.
  ENDMETHOD.


  METHOD read_data_by_request.
    DATA lo_root_filter_node TYPE REF TO /iwbep/if_cp_filter_node.

    TRY.
        DATA(lo_request) = get_proxy( )->create_resource_for_entity_set( c_entity )->create_request_for_read( ).

        DATA(lt_filter_condition) = io_request->get_filter( )->get_as_ranges( ).
        DATA(lt_requested_elements) = io_request->get_requested_elements( ).
        DATA(lt_sort_elements) = io_request->get_sort_elements( ).
        DATA(ld_skip) = io_request->get_paging( )->get_offset( ).
        DATA(ld_top) = io_request->get_paging( )->get_page_size( ).
        DATA(ld_is_data_requested)  = io_request->is_data_requested( ).
        DATA(ld_is_count_requested) = io_request->is_total_numb_of_rec_requested( ).

        DATA(lo_filter_factory) = lo_request->create_filter_factory( ).
        LOOP AT lt_filter_condition INTO DATA(ls_filter_condition).
          DATA(lo_filter_node) = lo_filter_factory->create_by_range( iv_property_path = ls_filter_condition-name
                                                                     it_range         = ls_filter_condition-range ).

          IF lo_root_filter_node IS INITIAL.
            lo_root_filter_node = lo_filter_node.
          ELSE.
            lo_root_filter_node = lo_root_filter_node->and( lo_filter_node ).
          ENDIF.
        ENDLOOP.

        IF lo_root_filter_node IS NOT INITIAL.
          lo_request->set_filter( lo_root_filter_node ).
        ENDIF.

        IF lt_requested_elements IS NOT INITIAL.
          lo_request->set_select_properties( CORRESPONDING #( lt_requested_elements ) ).
        ENDIF.

        IF lt_sort_elements IS NOT INITIAL.
          lo_request->set_orderby( CORRESPONDING #( lt_sort_elements MAPPING property_path = element_name ) ).
        ENDIF.

        IF ld_is_data_requested = abap_true.
          lo_request->set_skip( CONV #( ld_skip ) ).

          IF ld_top > 0.
            lo_request->set_top( CONV #( ld_top ) ).
          ENDIF.
        ELSE.
          lo_request->request_no_business_data( ).
        ENDIF.

        IF ld_is_count_requested = abap_true.
          lo_request->request_count( ).
        ENDIF.

        DATA(lo_response) = lo_request->execute( ).
        lo_response->get_business_data( IMPORTING et_business_data = et_result ).
        ed_count = lo_response->get_count( ).

      CATCH cx_root.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

 

Da wir bereits in einem Artikel die Verarbeitung behandelt haben, kann dort der Aufruf und die Funktionsweise nachgelesen werden. Im nächsten Schritt definieren wir die Verhaltensdefinition, um die Funktionen des RAP Objekts zu beschreiben und später ausprägen zu können:

unmanaged implementation in class zbp_bs_demo_company_names unique;
strict ( 1 );

define behavior for ZBS_R_RAPCustomCompanyNames alias CompanyNames
lock master
authorization master ( instance )
{
  field ( readonly : update ) CompanyName;

  create;
  update;
  delete;
}

 

Das Objekt steht auf Unmanaged und wir setzen den Schlüssel auf READONLY, wenn wir die Instanz aktualisieren. Im Anschluss generieren wir die leere Verhaltensimplementierung, damit das Objekt konsistent ist. Damit wir die Anwendung im Fiori Elements Preview aufrufen können, müssen wir noch eine Service Definition und ein Service Binding anlegen. Nach dem "Publish" des Services, in diesem Fall ein OData v2 Service, können wir zum ersten Mal die Anwendung starten.

 

Ausführung

Wenn wir nun einmal die Anwendung mit einem Klick auf "Go" starten, werden alle Datensätze On-Premise abgerufen und uns angezeigt.

 

Über den Filter können wir nun die Datenmenge eingrenzen und so zum Beispiel nach der passenden Firma suchen.

 

Mit einem Klick auf die Position navigieren wir auf die Object-Page bzw. Detailseite und sehen auch den Langtext. Über die Annotation "UI.multiLineText: true" verwandeln wir das einfache Eingabefeld in eine Textbox und können so mehr Text sehen.

 

Datenverwaltung

Wie bereits beim letzten Unmanaged Szenario für das lokale Szenario, müssen wir auch hier alle Operationen selbst implementieren. Dazu legen wir in der Verhaltensimplementierung ein Puffer Objekt an, um unsere Daten zu sichern:

CLASS lcl_data_buffer DEFINITION.
  PUBLIC SECTION.
    TYPES tt_data TYPE STANDARD TABLE OF ZBS_R_RAPCustomCompanyNames WITH EMPTY KEY.

    CLASS-DATA gt_create TYPE tt_data.
    CLASS-DATA gt_update TYPE tt_data.
    CLASS-DATA gt_delete TYPE tt_data.
ENDCLASS.

 

Bei den CRUD Operationen müssen wir nun entsprechend den Puffer befüllen, dabei müssen wir beim Update die Daten noch einmal Remote nachlesen oder wir implementieren die READ Methode, um über EML an die Daten zu kommen. In der SAVE Methode müssen wir uns zum Abschluss nun um die schreibenden Zugriff Richtung On-Premise System kümmern. Die fertige Implementierung könnte wir folgt aussehen:

DATA ls_remote_create TYPE zbs_rap_companynames.
DATA ls_remote_update TYPE zbs_rap_companynames.

LOOP AT lcl_data_buffer=>gt_create INTO DATA(ls_create).
  ls_remote_create = CORRESPONDING #( ls_create ).

  DATA(lo_request_create) = zcl_bs_demo_custom_company_qry=>get_proxy( )->create_resource_for_entity_set(
      zcl_bs_demo_custom_company_qry=>c_entity )->create_request_for_create( ).
  lo_request_create->set_business_data( ls_remote_create ).
  lo_request_create->execute( ).
ENDLOOP.

LOOP AT lcl_data_buffer=>gt_update INTO DATA(ls_update).
  ls_remote_update = CORRESPONDING #( ls_update ).
  DATA(ls_key) = VALUE zbs_rap_companynames( companyname = ls_remote_update-CompanyName ).

  DATA(lo_request_update) = zcl_bs_demo_custom_company_qry=>get_proxy( )->create_resource_for_entity_set(
      zcl_bs_demo_custom_company_qry=>c_entity
    )->navigate_with_key( ls_key
    )->create_request_for_update( /iwbep/if_cp_request_update=>gcs_update_semantic-put ).
  lo_request_update->set_business_data( ls_remote_update ).
  lo_request_update->execute( ).
ENDLOOP.

LOOP AT lcl_data_buffer=>gt_delete INTO DATA(ls_delete).
  DATA(ls_key_delete) = VALUE zbs_rap_companynames( companyname = ls_delete-CompanyName ).

  DATA(lo_request_delete) = zcl_bs_demo_custom_company_qry=>get_proxy( )->create_resource_for_entity_set(
      zcl_bs_demo_custom_company_qry=>c_entity
    )->navigate_with_key( ls_key_delete
    )->create_request_for_delete( ).
  lo_request_delete->execute( ).
ENDLOOP.

CLEAR: lcl_data_buffer=>gt_create, lcl_data_buffer=>gt_update, lcl_data_buffer=>gt_delete.

 

Hinweis: Bei der Implementierung haben wir bewusst auf die Fehlerbehandlung verzichtet, um die reinen Zugriffe und Aufrufe abzubilden. Eine saubere Behandlung von Ausnahmen und Fehlern beim Aufruf sollte unter allen Umständen implementiert werden.

 

Komplettes Beispiel

Das komplette Beispiel findest du wie immer in unserem GitHub Repository, über den Commit gelangst du zu den durchgeführten Änderungen und neuen Objekten.

 

Fazit

Die Implementierung eines Unmanaged Szenarios über eine Custom Entity ist etwas anders, als ein normales RAP Objekt aufzubauen. Durch diesen und die verlinkten Artikel sollte das für dich aber kein Problem mehr sein. Dieser Ansatz ist ebenso die Grundlage für hybride Anwendungen auf dem ABAP Environment.


Enthaltene Themen:
RAPBTPUnmanagedRemote
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


RAP - Excel Datei laden

Kategorie - ABAP

In diesem praktischen Beispiel schauen wir uns die Verarbeitung von Excel in ABAP mit den neuen XCO Klassen an und wie wir die Datei in unser RAP Objekt bekommen.

20.12.2024

RAP - Semantischer Schlüssel

Kategorie - ABAP

Wofür benötigst du den semantischen Schlüssel und wie wird er im ABAP RESTful Programming Model dargestellt? Hier gibt es mehr dazu.

13.12.2024

RAP - Upload von Dateien (Stream)

Kategorie - ABAP

Wie kannst du einfach Dateien in deine RAP Entität laden und diese in ABAP zur Verfügung stellen? Hier schauen wir uns einmal die Details an.

10.12.2024

RAP - Report Pattern

Kategorie - ABAP

Wie ist das Report Pattern in RAP aufgebaut und was kannst du damit machen? Mehr dazu in diesem ABAP Artikel.

06.12.2024

RAP - Übersetzungs-App (Beispiel)

Kategorie - ABAP

Schauen wir uns einmal ein praktisches Beispiel für die Entwicklung einer RAP Anwendung im ABAP Environment an und wie du mit wenig Aufwand eine App erstellst.

02.08.2024