
BTP - Dynamic Tile Service (Teil 2)
In diesem Artikel möchten wir ein Update zum Tile Service geben und wie du den Service noch leichter integrieren kannst. Dabei spielt die Custom Entity eine entscheidende Rolle.
Inhaltsverzeichnis
In diesem Artikel gehen wir auf eine zusätzliche Variante des Tile Service ein und wie du sie noch einfacher in deinen bestehenden Service integrieren kannst.
Einleitung
Im ersten Teil sind wir auf die verschiedenen Probleme und Möglichkeiten eingegangen und möchten uns in diesem Artikel eine weitere Möglichkeit anschauen. Dabei haben wir im ersten Teil vor allem auf den HTTP Service und die flexible Nutzung des Services gesetzt. Schauen wir uns vielleicht noch eine bessere Lösung zu dem Thema an.
Community
Die Community ist nicht ruhig, wenn es um Lösungen und Kreativität geht. Nach unserem Artikel zum Tile Service hatte Andre Fischer noch eine Lösung für das Problem mit Namenskonflikten in OData Services gegeben. Im Nachgang hatten Clément Ringot einen Blog auf dem Abapeur veröffentlicht, um zu testen, ob es noch längere Felder gibt, als die Beschränkung im DDIC vorgibt. Den Abschluss hatte dann Jörg Brandeis gemacht und das Thema als Core Data Service gepostet und wie du damit ein Dynamic Tile bauen kannst, leider ohne Übersetzbarkeit. Also jede Menge Community Spirit um eigentlich ein ähnliches Thema.
HTTP Service
Die Lösung als HTTP Service ist valide und flexibel gebaut. Allerdings sollten wir hier immer auf die Sicherheit achten und zusätzlich müssen wir den HTTP Service entsprechend zusätzlich für den User berechtigen. Das Thema Berechtigungen und Nutzung ist nicht unbedingt einfach und immer so flexibel pro Anwendung steuerbar.
Lösung
Daher schauen wir uns eine zweite Lösung auf Basis einer Custom Entity an und gehen die Lösung plus Konfiguration Schritt für Schritt durch.
Vorbereitung
Als Vorbereitung brauchen wir eigentlich nur die verschiedenen Informationen der Community Artikel und erstellen uns unsere finale Lösung.
Custom Entity
Herzstück des Service ist dabei eine Custom Entity, die wieder der Struktur entsprechen muss, wie in der SAP Help Dokumentation beschrieben. Dabei benennen wir das Feld "number" erst einmal "numberOutput" und konvertieren das Feld mit Hilfe der Annotation "@OData.property.name" im OData Service dann auf unseren gewünschten Namen.
@EndUserText.label: 'Custom Tile'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_BS_DEMO_TILE_QUERY'
define custom entity ZBS_I_DMOCustomTile
{
key key_field : abap.char(20);
title : abap.string;
subtitle : abap.string;
icon : abap.string;
@OData.property.name: 'number'
numberOutput : abap.dec( 15, 4 );
numberDigits : abap.int2;
numberFactor : abap.string;
numberState : abap.string;
numberUnit : abap.string;
stateArrow : abap.string;
info : abap.string;
infoState : abap.string;
}
Kachel
Im nächsten Schritt erweitern wir dann die Service Definition und nehmen die Custom Entity mit auf. Für einen leichteren Zugriff über den Service geben wir der Entität einen kürzeren Namen, in diesem Fall "Tile". Damit können wir später über den gleichen Service auf die Konfiguration zugreifen, da wir den Service sowieso für unsere App berechtigen müssen.
@EndUserText.label: 'Simple Partner Service'
define service ZBS_SIMPLE_PARTNER {
expose ZBS_C_RAPPartner as Partner;
expose ZBS_I_RAPCityVH;
expose ZBS_I_DMOCustomTile as Tile;
}
Legen wir uns im App Descriptor Item noch eine zusätzliche Kachel an. Dazu erweitern wir diesen und legen ein neues "Dynamic" Tile an.
Als Service URL hinterlegen wir dann unseren OData Service und unsere neue Entität. Da wir diese Tile genannt haben, kommt dies hinter den Pfad. In die Klammern packen wir einen Schlüsselzugriff. Heißt nun, wir wollen den Eintrag mit genau diesem Schlüssel (PARTNER) lesen.
/sap/opu/odata/sap/ZBS_UI_SIMPLE_PARTNER_O2/Tile('PARTNER')
Implementierung
Damit sind die Grundlagen erst einmal definiert und wir können in die weiteren Details der Implementierung gehen. Da wir einen Schlüssel für den Service definiert haben, können wir dynamisch steuern, was wir dem Aufrufer als Information zurückgeben wollen. Dazu müssen wir allerdings auch den Schlüssel extrahieren, der als Filter an die Query Klasse übergeben wird. Dazu lesen wir die Filter, lesen unseren Schlüssel aus und überprüfen zur Sicherheit noch einmal den Wert, bevor wir diesen weiterverarbeiten.
TRY.
DATA(filters) = request->get_filter( )->get_as_ranges( ).
CATCH cx_rap_query_filter_no_range.
CLEAR filters.
ENDTRY.
TRY.
DATA(range) = filters[ name = `KEY_FIELD` ]-range.
DATA(value) = range[ 1 ]-low.
CATCH cx_sy_itab_line_not_found.
RETURN.
ENDTRY.
DATA(regex) = xco_cp=>regular_expression( iv_value = `^[A-Z0-9_]+$`
io_engine = xco_cp_regular_expression=>engine->pcre( ) ).
IF regex->matches( value ).
RETURN value.
ENDIF.
Im nächsten Schritt können wir dann wieder die Rückgabestruktur befüllen. Dazu rufen wir für unseren Schlüssel die entsprechende Methode auf. Hier können wir auch das gleiche Prinzip wie zuvor über das Interface und die Klassen verwenden. Wie du deine Klasse nutzen möchtest, bleibt am Ende dir überlassen. Zum Abschluss rufen wir die Methoden auf, um die Ergebnisse an den Aufrufer zu geben.
CASE identifier.
WHEN 'PARTNER'.
DATA(tile) = get_partner( ).
WHEN OTHERS.
CLEAR tile.
ENDCASE.
INSERT tile INTO TABLE tiles.
IF io_request->is_data_requested( ).
io_response->set_data( tiles ).
ENDIF.
IF io_request->is_data_requested( ).
io_response->set_total_number_of_records( 1 ).
ENDIF.
Wichtig ist aber auch, dass wir die Pflichtmethoden der Custom Entity aufrufen, da sonst der Aufruf in einem Dump endet. Daher implementieren wir noch den Aufruf, auch wenn wir auf die Ergebnisse verzichten und diese nicht nutzen. Weitere Details, wieso wir das machen, findest du in diesem Deep Dive.
io_request->get_sort_elements( ).
io_request->get_paging( )->get_offset( ).
io_request->get_paging( )->get_page_size( ).
Weitere Schritte
Damit haben wir eine weitere Lösung, um einen dynamischen Service zu erzeugen und damit eine Kachel mit Informationen zu versorgen. Die Übersetzung der Texte kannst du wieder über Textsymbole in der Klasse machen und bleibst damit International.
Grundsätzlich kannst du auch alles in einem Core Data Service modellieren, allerdings wird die Abbildung der Übersetzbarkeit dann etwas schwieriger, da du zusätzliche Ressourcen zur Verfügung stellen musst
Vollständiges Beispiel
Wie üblich findest du das komplette Beispiel im GitHub Repository zu den RAP Beispielen. In diesem Commit findest du die Anpassungen und neuen Objekte und kannst damit später noch alle Änderungen nachvollziehen. Als Ergebnis erhalten wir wieder ein dynamisches Tile, dass die Informationen dynamisch aus dem Service bezieht:
Fazit
In dem heutigen Artikel wollten wir noch einmal eine zweite Variante des Tile Service beleuchten und noch einige Vorteile aus einigen Zusatzinformationen ziehen. Wie du am Ende den Endpunkt baust, bleibt dir und deiner Kreativität überlassen.

