linked-swissbib / hydra-swissbib.ch

MIT License
4 stars 2 forks source link

subobject / adress #18

Open guenterh opened 5 years ago

guenterh commented 5 years ago

Das Ziel: ich möchte Adressen von Organisationen als eigenes Objekt anzeigen können (denke zumindest, dass es ein sinnvoller Weg ist, dies über ein Objekt (Entität) zu machen und dafür den Mechanismus von subobjects zu verwenden. Beispiel im Index: http://[server]/organisation/ABN-KH

Definition über Annotationen als API-Resource
/**
 * An Address
 *@see https://www.w3.org/2006/vcard/ns#
 * @ApiResource(
 *          subresourceOperations={
 *          "organization_get_subresource"={
 *              "method"="GET",
 *              "path"="/organisation/{id}"
 *          },
 *      },
 * )
 *
 *
 * @author   Günter Hipler <guenter.hipler@unibas.ch>>
 * @license  http://opensource.org/licenses/gpl-2.0.php
 * @link     http://linked.swissbib.ch

Dies funktioniert, das Objekt wird gefunden, ich bekomme jedoch ein Router Problem. Die Komponente twigs kann nicht die id doppelt anzeigen. Zwei Dinge gehen mir durch den Kopf:

beschreibe ich dies mit Hilfe eines yaml-Ressourcenfiles (config/api-platform/resources.yaml) dann bekomme ich die Fehlermeldung, dass die Ressource nicht gefunden werden kann. Habe bisher noch keinen Weg gefunden, dies abzustellen.

Setze ich die Annotation in die property des Objekts, erhalte ich den Fehler, dass die Annotation nicht gefunden werden kann. Ich stehe damit wohl noch auf Kriegsfuss...

    /**
     * @var Address
     * @ApiSubresource()
     */
    private $address;

Für mich das Ziel dieses tasks

guenterh commented 5 years ago

Ein paar Detailfragen

maechler commented 5 years ago

Hallo Günter

Ich habe mir erlaubt einige Dinge gleich im Code zu beheben und hinzuzufügen, da ich selber etwas testen musste wie das umzusetzen ist. Nachfolgend versuche ich deine Fragen zu beantworten und zu erklären, was ich gemacht habe.

Router Problem So wie ich das sehe, wird Twig zuerst einmal nicht verwendet, wenn die JSON-LD Serialisierung angefragt wird. Der Fehler stammt eigentlich vom Router, der die URL nicht sinnvoll auflösen kann, da zweimal der Parameter {id} vorkommt. Aufgrund dieses Fehlers wird versucht eine Fehlerseite mit Twig-Templates anzuzeigen, da scheint der selbe Fehler aber nochmals eine Exception zu verursachen. Deshalb kommt bei jeder Serialisierung schlussendlich ein Twig_Error_Runtime.

Ich habe daraufhin einfach mal im ganzen Projekt nach {id} gesucht und bin darauf gestossen, dass diese im CamelCaseResourcePathGenerator vorkommt. Der PathGenerator wird dazu verwendet einen Ressourcen-Namen in ein URL-Pfadsegment zu transformieren. Als ich unseren eigenen PathGenerator mit dem der ApiPlatform verglich, ist mir aufgefallen, dass dort keine {id} mehr verwendet wird. Das wird neuerdings ausserhalb des PathGenerator vom Framework übernommen. Daraufhin habe ich den CamelCaseResourcePathGenerator angepasst, siehe: https://github.com/linked-swissbib/hydra-swissbib.ch/commit/3c2ab3b7591ec77496bdfaf393bcc75047bfd401

Annotationen Der Fehler, dass die Annotation nicht gefunden werden kann, stammt von einem Tippfehler im Namespace. Es sollte ApiPlatform anstelle von APIPlatform sein, siehe: https://github.com/linked-swissbib/hydra-swissbib.ch/commit/2861c102507be0eb807a9aab0fb413fffc7b5640

Du siehst das absolut richtig, die Annotationen, die du verwenden kannst, findest du unter ApiPlatform\Core\Annotation. Die Klassen, die dort definiert sind, kannst du mit @Klassenname() verwenden. Das sind meines Wissens alle Annotation, die API Platform zur Verfügung stellt.

Die Eigenschaft requirements, die du erwähnst, ist ein untergeordnetes Element des Attributes itemOperations. Das Attribut itemOperations ist als beliebiger Array definiert (https://github.com/api-platform/core/blob/1f11969cd452b2a61cdbf36e93f1ddbe5478e9e1/src/Annotation/ApiResource.php#L40), das heisst es kann beliebige key => value Paare, wie z.B. requirements => ??? enthalten.

Die oberste Ebene der möglichen Attribute einer Annotation sind innerhalb der Klasse mit @Attributes definiert. Auf der ersten Ebene der Attribute einer Annotation hört das Schema aber bereits auf. Welche spezifischen Attribute es innerhalb des Arrays itemOperations gibt, kann meines Wissens nur noch über die Dokumentation (https://api-platform.com/docs/core/operations) oder direkt im Code nachvollzogen werden. Das angegebene XML Schema der Konfiguration geht leider auch nicht tiefer: https://github.com/api-platform/core/blob/master/src/Metadata/schema/metadata.xsd

Wenn ich im Code nach requirements Suche, dann stosse ich auf folgende Stelle: https://github.com/api-platform/core/blob/b4e5c213f5c10dac2fe05beff75464f06d0a6570/src/Bridge/Symfony/Routing/ApiLoader.php#L205-L219 Die Parameter werden mehr oder weniger einfach so an die Symfony Klasse Route durchgereicht. Die Dokumentation dazu findest du hier: https://symfony.com/doc/current/routing.html#adding-wildcard-requirements

Subresources Ja, ich denke auch, dass Subresources der richtige Weg sind um die Adressen abzubilden. Damit das funktioniert, muss ein SubresourceDataProvider implementiert werden. Ich habe dazu leider nichts in der Dokumentation gefunden, es zeigt sich wiedereinmal, dass die Dokumentation vorallem für den Standard-Fall gut ist, bei dem einfach eine Datenbank verwendet wird. Ich musste das direkt aus dem Code herausfinden.

Ich habe jetzt mal einen ElasticsearchSubresourceDataProvider implementiert, der einfach immer dieselbe Adresse ausgibt, siehe: https://github.com/linked-swissbib/hydra-swissbib.ch/commit/162a2566ca69a40e580170f29b4d7a8ad726a490 Dazu muss einerseits die Klasse implementiert werden und andererseits die Klasse als Service registriert und mit dem Tag api_platform.subresource_data_provider versehen werden. Dieser Tagging Mechanismus ist ein beliebtes Pattern bei der ApiPlatform, das du kennen solltest. Jeder DataProvider, der vom Framework beachtet werden soll, muss als Service registriert sein und mit dem entsprechenden Tag gekennzeichnet sein. Das Framework lädt dann alle DataProvider und fragt diese nacheinander ab, ob sie für die angefragte Ressource eine Antwort liefern können. Sobald ein DataProvider eine Antwort liefert, wird die Schleife abgebrochen.

Mit dem neusten Stand des Branch api.2.3_subresource sollte es möglich sein folgende URL aufzurufen und die Dummy Adresse als Antwort zu erhalten (Caches löschen vorher): http://localhost/organisation/ABN-BBB/address

Todos Ich habe einige Todos hinterlassen:

liowalter commented 5 years ago

[hydra-swissbib.ch] subobject / adress

guenterh commented 5 years ago

Hallo Markus, (@maechler)

hier eine Zusammenfassung meiner Gedanken:

Was ich dewwegen als nächstes machen möchte ist: zurück zu den Daten, diese nochmals anschauen und auf Basis der API Plattform prüfen, welche Modellierungsmöglichkeiten gegeben sind oder eben nicht. Ich möchte die Fragen beantworten:

Damit zum Abschluss: Lass uns als nächstes die Varianten der Modellierung in der API-Plattform durchspielen (und ausprobieren). Ich möchte jetzt wiessen was geht und was nicht, bzw müsste man mit eigener Implementierung erweitern. Dies ist die Implementierungsseite. Mit @sschuepbach, @Kordishal sowie @witzigs und @liowalter möchte ich mir nochmals die Modellierungsseite und die aktuellen und zukünftigen use-cases ansehen.

Ist das für Dich ein Weg und in Ordnung? Was geht Dir dazu durch den Kopf? Wenn Du das für richtig hälst, schicke ich Dir bald mehr Details.

maechler commented 5 years ago

Hallo @guenterh

Mein Gesamteindruck ist, dass wir relativ wenig Funktionalität der API-Platform wirklich nutzen. Das Framework ist vor allem stark wenn eine Datenbank verwendet wird, wenn Daten über ein UI bearbeitet werden sollen (Admin Component) und die Daten z.B. von einer Client-Applikation (React oder Vue.js) konsumiert werden. In meinen Augen das Wichtigste, das wir vom Framework verwenden, ist die Anreicherung mit Hydra und die Dokumentation der Schnittstelle. Zusätzlich gibt es eine sinnvolle Struktur vor mit dem DataProvider und Encoder, das ist aber nichts, das man nicht auch nachbauen könnte. Mit ein Grund für die Entscheidung für dieses Framework war natürlich auch die Idee, dass Komponenten (sowie der ElasticsearchAdapter) für VuFind wieder verwendet werden können. Wenn jetzt Hydra keine Anforderung mehr wäre oder man das Framework weiter aufbohren müsste um den Kontext direkt aus Elasticsearch zu übernehmen, dann stellt sich wirklich die Frage, ob das noch die richtige Technologie ist.

Ich finde den Weg, denn du vorschlägst sinnvoll. Ich gehe jetzt davon aus, dass ich mir die folgenden Fragen noch nicht ansehen soll sondern, dass ich noch auf weitere Details von dir warte.

Ich kannte den Einbezug der RDF Daten in swissbib.ch noch nicht, das ist ein schönes Feature!

guenterh commented 5 years ago

Hallo @maechler

Danke für Dein Feedback. Ich habe gerade nochmals in einem Dokument von Dir nachgesehen. In unserer Diskussion wurden seinerzeit 11 funktionale Hauptanforderungen (die meisten davon mit weiteren Unterteilungen) und 5 nicht-funktionale Anforderungen aufgestellt. Beim Durchlesen bin ich erneut zum Schluss gekommen, dass Du und Melanie die Projektziele zu 100% und mehr erreicht habt. Wie Du mit Deinem letzten Kommentar erwähnst, vor allem die Abstraktion der Backendservices (bei uns Elasticsearch). Potentiell hatten wir damals auch daran gedacht, mit dieser API ein Frontend für andere services wie Graphendatenbanken anzubieten. Ein solcher data-provider könnte gut in die von Euch entworfene Struktur eingebaut werden.

Mit diesem sehr generellen Ansatz sind wir, so mein Eindruck, aber auch an die Grenzen der aktuellen API-Platform gestossen. Diese versucht "allgemeinen" Datenstrukturen (im Falle der default-Implementation Daten im relationalen Modell) als response eines requests mit dem hydra-Vokabular anzureichern, um damit maschinelle, nicht auf eine bestimmte API fokussierte, clients zu ermöglichen. Wir haben das damals "Selbstdokumentation" der API genannt, so dass clients, bzw. die sie entwicklenden Menschen nicht erst die Dokumentation der Schnittstlle lesen müssen. Grundsätzlich stehe ich immer noch hinter dieser Idee (und wir werden auch von aderen dabei gestützt, z.B. https://ruben.verborgh.org/blog/2013/11/29/the-lie-of-the-api/ jemand der im Semantic web sehr aktiv ist)

Vor allem mir war damals auch sehr wichtig, dass wir es mit der Implementierung auch schaffen, Grundlagen für die Integration es ES servers in das VuFind Universum zu legen. (Steht so gar nicht mal in Deinem Anforderungsdokument). Aus diesem Gedanken ist dann der ESAdapter entstanden. Dieser wurde dann ja auch bei der Integration der linked-artefakte in https://www.swissbib.ch verwendet. Last but not least war dieses Kriterium dann auch ausschlaggebend dafür, dass wir uns für PHP als die Implementierungssprache entschieden haben.

So, ich denke, damit ist das Bild einigermassen abgerundet und auch für Dritte grundsätzlich nachvollziehbar.

Nicht nur aber auch nach dieser Diskussion ziehe ich folgende Schlüsse:

Vielen Dank Markus, dass wir das hier sehr offen diskutieren konnten. Ich möchte anderen vor allem im swissbib Universum damit die Möglichkeit geben, unsere Gedanken möglichst gut nachvollziehen zu können. Ich melde mich noch auf anderen Kanälen.