Textreduktion

This commit is contained in:
2026-09-07 23:37:09 +02:00
parent 3ecc28ef73
commit 235b471ad9
39 changed files with 205 additions and 553 deletions
+6 -20
View File
@@ -3,7 +3,7 @@
\subsection{Schichtung}
Das Dokumentenmodul folgt der in Houston etablierten Schichtung. Abbildung~\ref{fig:module-components} zeigt die Komponenten.
Das Dokumentenmodul folgt der in Houston etablierten Schichtung (Abbildung~\ref{fig:module-components}).
\begin{figure}[H]
\centering
@@ -12,33 +12,19 @@ Das Dokumentenmodul folgt der in Houston etablierten Schichtung. Abbildung~\ref{
\label{fig:module-components}
\end{figure}
Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Suche, Filter und Blätterelemente, \texttt{Document/Download} für Downloadanforderungen (einzeln und als ZIP) und \texttt{Document/Share} für Freigabelinks. Die Aufteilung folgt daraus, dass Download und Freigabe eigene Routen mit eigenen Antwortformaten benötigen.
Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts: Auflösung des Kundenordners, Typableitung aus dem Pfad, Ausblendung technischer Einträge. Der \texttt{S3DocumentsClient} kapselt den technischen Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
Die oberste Schicht bilden drei Razor Pages: \texttt{Documents} für Liste, Suche, Filter und Blätterelemente, \texttt{Document/Download} für Downloads (einzeln und als ZIP) und \texttt{Document/Share} für Freigabelinks. Darunter liegt der \texttt{DocumentsService} als fachliche Schicht mit den Regeln des Ablagekonzepts (Kundenordner-Auflösung, Typableitung, Ausblendung technischer Einträge). Der \texttt{S3DocumentsClient} kapselt den Speicherzugriff und ist die einzige Stelle, an der \texttt{IAmazonS3} unmittelbar verwendet wird.
\subsection{Eigene Typen statt Zeichenketten}
\label{sec:newtype}
Eine Entwurfsentscheidung, die sich im Verlauf herausbildete, betrifft den Umgang mit Pfaden. Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde.
Anfangs wurden Dokumentschlüssel als Zeichenketten durch die Schichten gereicht. Im Review des Downloads führte das zu wiederholten Rückfragen zur Pfadvalidierung, weil einer Zeichenkette nicht anzusehen ist, ob sie bereits geprüft wurde. Als Ausweg verpackte ich die Pfade nach dem \emph{Newtype-Pattern} in eigene \texttt{readonly record struct}-Typen \autocite{rust-newtype}, ergänzt um „parse, don’t validate“ \autocite{king-parse}: Wer einen \texttt{DocumentKey} entgegennimmt, muss die Gültigkeit nicht erneut prüfen.
Der Ausweg war ein Muster, das ich außerhalb der Arbeit beim Programmieren in Rust kennengelernt habe: das \emph{Newtype-Pattern}, umgesetzt als \texttt{readonly record struct} ohne Laufzeitkosten \autocite{rust-newtype}. Ein primitiver Wert wird in einen eigenen Typ verpackt, damit der Übersetzer zwei Werte unterscheidet, die als Zeichenkette identisch aussehen. Ergänzt wird das durch die Haltung „parse, don’t validate“ \autocite{king-parse}: Eine Funktion, die einen \texttt{DocumentKey} entgegennimmt, muss die Gültigkeit nicht erneut prüfen — sie wäre sonst gar nicht aufrufbar gewesen.
So entstanden \texttt{DocumentKey} für den vollständigen Pfad einschließlich Kundenordner (Listing~\ref{lst:document-key}) und \texttt{DocumentRelativePath} für den Anteil darunter, beide nur über eine prüfende Fabrikmethode erzeugbar; eine vergessene Prüfung führt so zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName}, \texttt{DocumentId} und \texttt{OrgSlug}.
Der Datenfluss folgt daraus unmittelbar: Aus der Anfrage kommt eine Zeichenkette, der Speicher liefert Schlüssel als Zeichenketten zurück, beide werden einmal am Rand in das Domänenmodell geparst. Die gesamte weitere Verarbeitung — Typableitung, Filterung, Sortierung, Blätterung — arbeitet nur noch auf Typen. Erst wenn ein weiterer Speicheraufruf nötig ist, wird aus dem Modell wieder ein Schlüssel erzeugt. Zeichenketten existieren damit ausschließlich an den Systemgrenzen.
Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring — ein Aufwand, der sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
So entstanden \texttt{DocumentKey} für den vollständigen Pfad einschließlich Kundenordner (Listing~\ref{lst:document-key}) und \texttt{DocumentRelativePath} für den Anteil darunter, beide nur über eine prüfende Fabrikmethode erzeugbar; eine vergessene Prüfung führt so zu einem Übersetzungsfehler statt zu einer Sicherheitslücke. Nach demselben Muster entstanden \texttt{DocumentType}, \texttt{DocumentName}, \texttt{DocumentId} und \texttt{OrgSlug}. Zeichenketten werden einmal am Rand in diese Typen geparst; die weitere Verarbeitung arbeitet nur noch auf Typen. Der zugehörige Pull Request durchlief 23 Iterationen und bestand zu einem erheblichen Teil aus diesem Refactoring, das sich auszahlte, weil ZIP-Download, PDF-Vorschau und Freigabelinks dieselben Typen wiederverwenden konnten.
\subsection{Zentrale Autorisierung}
Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration erfolgt und eine zusätzliche Prüfung an der Seite überflüssig ist.
Der Einwand betrifft mehr als doppelten Code: Eine seitenspezifische Prüfung ist leicht zu übersehen, wenn weitere Seiten hinzukommen — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt sicher, dass alle Routen des Moduls derselben Richtlinie unterliegen.
Die erste Fassung des Document Explorers prüfte die Berechtigung im PageModel. Im Review wies \emph{Hanna Ebner} darauf hin, dass die Autorisierung in Houston zentral in der Anwendungskonfiguration erfolgt und eine zusätzliche Prüfung an der Seite überflüssig ist. Eine seitenspezifische Prüfung ist zudem leicht zu übersehen, wenn weitere Seiten hinzukommen — genau das wäre bei \texttt{Download} und \texttt{Share} passiert. Die zentrale Registrierung stellt sicher, dass alle Routen derselben Richtlinie unterliegen.
\subsection{Gestapelte Pull Requests}
Ein Vorgehen, das sich durch die gesamte Umsetzung zieht, ist die Verkettung der Pull Requests. Der erste ging gegen den Hauptbranch; jeder darauf aufbauende gegen den Branch seines Vorgängers.
Der Grund ist die Reviewbarkeit. Da die Arbeiten aufeinander aufbauen, hätte ein direkter Vergleich gegen den Hauptbranch auch sämtliche Vorgängeränderungen enthalten. Durch die Verkettung enthält jeder Pull Request genau die Änderungen seines Backlog Items.
Der Preis zeigte sich beim Zusammenführen: Sobald ein Vorgänger in den Hauptbranch übernommen war, musste der Nachfolger umgestellt werden. Diese Umstellung setzt in Azure DevOps die bereits abgegebenen Freigaben zurück. Für künftige Arbeiten wäre abzuwägen, ob der Gewinn an Reviewbarkeit diesen Abstimmungsaufwand rechtfertigt; bei der vorliegenden Zahl aufeinander aufbauender Items überwog er deutlich.
Die Pull Requests wurden verkettet: Der erste ging gegen den Hauptbranch, jeder darauf aufbauende gegen den Branch seines Vorgängers. So enthält jeder Pull Request genau die Änderungen seines Backlog Items, was die Reviewbarkeit erhöht. Der Preis zeigte sich beim Zusammenführen: Sobald ein Vorgänger übernommen war, musste der Nachfolger umgestellt werden, was in Azure DevOps die bereits abgegebenen Freigaben zurücksetzt. Bei der vorliegenden Zahl aufeinander aufbauender Items überwog der Gewinn an Reviewbarkeit diesen Aufwand deutlich.
+5 -17
View File
@@ -1,30 +1,18 @@
\section{Document Explorer}
\label{sec:document-explorer}
\subsection{Umfang}
\subsection{Umfang und Darstellung}
Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten das umfangreichste. Er umfasst die Grundstruktur des gesamten Moduls: Seite, Dienst, Speicherclient, Modelle sowie Einbindung in Navigation und Autorisierung.
\subsection{Vom Kachel- zum Listenentwurf}
Die ursprüngliche Fassung sah eine Kacheldarstellung vor. Im Verlauf wurde auf Zeilendarstellung geändert, da Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschau im Backlog standen. Eine Kachel hätte für diese Elemente keinen natürlichen Platz geboten. Die Tabelle soll flexibel um weitere Spalten erweiterbar sein.
Der Document Explorer war das erste umgesetzte Backlog Item und mit acht Aufwandspunkten das umfangreichste. Er umfasst die Grundstruktur des Moduls: Seite, Dienst, Speicherclient, Modelle sowie Einbindung in Navigation und Autorisierung. Die ursprüngliche Kacheldarstellung wurde auf Zeilendarstellung geändert, da Auswahlboxen für den ZIP-Download, Download- und Teilen-Schaltflächen sowie eine Vorschau im Backlog standen, für die eine Kachel keinen natürlichen Platz geboten hätte; die Tabelle ist zudem um weitere Spalten erweiterbar.
\subsection{Ableitung des Dokumententyps}
Der Typ eines Dokuments ergibt sich aus dem Unterordnernamen und wird gegen eine Aufzählung abgeglichen. Im Review schlug \emph{Timo Walter} vor, die Zeichenketten-Umwandlung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden statt einer eigenen Zuordnung. Beim Hinzufügen eines Typs muss so nur die Aufzählung ergänzt werden. Schlägt die Umwandlung fehl, gilt das Dokument als untypisiert.
Eine bewusste Ausnahme betrifft die Übersetzung: Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird eine Ausnahme ausgelöst. Jeder Wert der Aufzählung ist per Definition gültig; fehlt seine Übersetzung, ist das eine unvollständige Implementierung. Ein stiller Rückfall auf den Ordnernamen würde diesen Fehler verdecken.
Der Typ eines Dokuments ergibt sich aus dem Unterordnernamen und wird gegen eine Aufzählung abgeglichen. Im Review schlug \emph{Timo Walter} vor, die Zeichenketten-Umwandlung mit Nichtbeachtung der Groß- und Kleinschreibung zu verwenden statt einer eigenen Zuordnung; beim Hinzufügen eines Typs muss so nur die Aufzählung ergänzt werden. Schlägt die Umwandlung fehl, gilt das Dokument als untypisiert. Eine bewusste Ausnahme betrifft die Übersetzung: Fehlt für einen bekannten Typ der Eintrag in der Ressourcendatei, wird eine Ausnahme ausgelöst statt still auf den Ordnernamen zurückzufallen — eine fehlende Übersetzung ist eine unvollständige Implementierung.
\subsection{Leerer Zustand und Fehlerbehandlung}
Zwei Anforderungen betreffen Situationen ohne anzeigbare Dokumente und sind zu unterscheiden.
Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber keine Dokumente vorliegen — insbesondere unmittelbar nach der automatischen Ordneranlage. Er wird mit einem Hinweis dargestellt.
Ein \textbf{Fehler} liegt vor, wenn die Dokumente nicht geladen werden konnten. Im Review wurde angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf: Im einen Fall gibt es nichts zu sehen, im anderen funktioniert die Anwendung nicht.
Zwei Situationen ohne anzeigbare Dokumente sind zu unterscheiden. Der \textbf{leere Zustand} tritt ein, wenn der Kunde berechtigt ist, aber keine Dokumente vorliegen — insbesondere unmittelbar nach der automatischen Ordneranlage; er wird mit einem Hinweis dargestellt. Ein \textbf{Fehler} liegt vor, wenn die Dokumente nicht geladen werden konnten; im Review wurde angemerkt, dass dieser Fall nicht in einer stillschweigend leeren Seite münden darf.
\subsection{Die bekannte Schwäche}
Bereits im Review fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage nötig sei. Die Antwort war, dass die Schnittstelle dies nicht anders zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item.
Die Schwäche wurde erkannt und bewusst zurückgestellt — der Explorer sollte nicht daran scheitern, dass eine Optimierung noch nicht gefunden war. Die tatsächliche Lösung beschreibt Abschnitt~\ref{sec:folder-management}.
Bereits im Review fragte \emph{Hanna Ebner}, warum für jeden Kunden eine eigene Abfrage nötig sei. Die Antwort war, dass die Schnittstelle dies nicht anders zulässt, verbunden mit dem Verweis auf das eigens angelegte Recherche-Item. Die Schwäche wurde bewusst zurückgestellt; die tatsächliche Lösung beschreibt Abschnitt~\ref{sec:folder-management}.
+4 -10
View File
@@ -3,17 +3,11 @@
\subsection{Einzeldownload über zeitlich begrenzte Zugriffs-URLs}
Für den Einzeldownload wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}. Die Datenübertragung läuft nicht über die Anwendung; ein großes Dokument belegt weder Arbeitsspeicher noch Verbindungen des Webservers. Die URL ist nur wenige Minuten gültig.
Wichtig ist, dass die Berechtigungsprüfung \emph{vor} dem Erzeugen der URL stattfindet. Die URL selbst trägt keine Prüfung — wer sie besitzt, kann zugreifen. Sie darf deshalb nur für einen Schlüssel erzeugt werden, der nachweislich im Kundenordner liegt. Genau hier setzte der überwiegende Teil des Reviews an, und die Rückfragen führten zur in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen.
Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt, da der Schlüssel den vollständigen Pfad enthält.
Für den Einzeldownload wird eine zeitlich begrenzte Zugriffs-URL erzeugt, mit der der Browser das Dokument unmittelbar vom Speicher abruft \autocite{aws-presigned-urls}. Die Datenübertragung läuft nicht über die Anwendung, und die URL ist nur wenige Minuten gültig. Die Berechtigungsprüfung findet \emph{vor} dem Erzeugen der URL statt, da die URL selbst keine Prüfung trägt: Sie darf nur für einen Schlüssel erzeugt werden, der nachweislich im Kundenordner liegt. Genau hier setzte der überwiegende Teil des Reviews an und führte zur in Abschnitt~\ref{sec:architecture} beschriebenen Einführung eigener Pfadtypen. Zusätzlich musste sichergestellt werden, dass das Dokument unter seinem ursprünglichen Namen ankommt.
\subsection{ZIP-Download}
Jede Zeile erhält eine Auswahlbox für den ZIP-Download. Die Schaltfläche ist deaktiviert, solange nichts ausgewählt ist.
Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
Jede Zeile erhält eine Auswahlbox für den ZIP-Download; die Schaltfläche ist deaktiviert, solange nichts ausgewählt ist. Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
\begin{figure}[H]
\centering
@@ -22,8 +16,8 @@ Abbildung~\ref{fig:zip-stream} zeigt den Ablauf.
\label{fig:zip-stream}
\end{figure}
Das Archiv wird erst beim Klick erzeugt. Entscheidend ist, dass es nie vollständig im Arbeitsspeicher entsteht: Die Anwendung öffnet die Antwort als Archivstrom, liest die Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Da die Gesamtgröße unbegrenzt ist, würde ein vollständiger Aufbau den Speicherbedarf von der größten Auswahl abhängig machen. Beim Strömen bleibt er konstant.
Das Archiv wird erst beim Klick erzeugt und entsteht nie vollständig im Arbeitsspeicher: Die Anwendung öffnet die Antwort als Archivstrom, liest die Dokumente nacheinander aus dem Speicher und schreibt sie unmittelbar als Einträge hinein \autocite{dotnet-zip}. Da die Gesamtgröße unbegrenzt ist, würde ein vollständiger Aufbau den Speicherbedarf von der größten Auswahl abhängig machen; beim Strömen bleibt er konstant.
\subsection{ZIP auch bei einer einzelnen Datei}
Im Review stellte \emph{Sarah Hinzmann} die Frage, ob bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle. Die Entscheidung fiel für das Archiv: Die Schaltfläche ist mit „Als ZIP herunterladen" beschriftet und soll vorhersagbar ein Archiv liefern. Wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der Zeile.
Im Review fragte \emph{Sarah Hinzmann}, ob bei nur einer ausgewählten Datei ein Archiv erzeugt werden solle. Die Entscheidung fiel für das Archiv: Die Schaltfläche „Als ZIP herunterladen" soll vorhersagbar ein Archiv liefern; wer eine einzelne Datei unverpackt benötigt, verwendet die Download-Schaltfläche in der Zeile.
+3 -13
View File
@@ -3,22 +3,12 @@
\subsection{Filterung nach Dokumententyp}
Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im PageModel ausgewertet; die Filterung selbst erfolgt anschließend im Dienst.
Die Auswertung des Parameters war Gegenstand des Reviews. Da die Werte aus der Adresszeile stammen, muss die Anwendung mit manipulierten oder veralteten Werten rechnen. \emph{Robin Noack} schlug vor, nicht erkennbare Werte stillschweigend zu verwerfen. Ein unbekannter Filterwert ist kein Angriff, sondern in der Regel ein veralteter Link. Die Mandantentrennung geschieht über das Präfix, nicht über den Filter.
Ein zweiter Reviewhinweis betraf die Benennung: Der Typ für eine Menge von Dokumententypen unterschied sich vom Einzeltyp nur durch ein Zeichen. Da eine Verwechslung leicht möglich ist, wurde die Benennung geschärft.
Unterhalb der Suchleiste steht je Dokumententyp ein Bedienelement zum Ein- und Ausblenden. Die Auswahl wird als Abfrageparameter übertragen und im Dienst ausgewertet. Da die Werte aus der Adresszeile stammen, muss die Anwendung mit manipulierten oder veralteten Werten rechnen; \emph{Robin Noack} schlug vor, nicht erkennbare Werte stillschweigend zu verwerfen — ein unbekannter Filterwert ist in der Regel ein veralteter Link, und die Mandantentrennung geschieht über das Präfix, nicht über den Filter. Ein zweiter Reviewhinweis führte zu einer geschärften Benennung, da sich der Typ für eine Menge von Dokumententypen vom Einzeltyp nur durch ein Zeichen unterschied.
\subsection{Paginierung mit Fortsetzungsmerkmalen}
Die Paginierung folgt der Arbeitsweise des Speichers, der Ergebnisse blockweise mit Fortsetzungsmerkmalen liefert \autocite{aws-listobjectsv2}. Ein Fortsetzungsmerkmal setzt die Auflistung dort fort, wo sie endete. Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen. Die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren Vor- und Zurück-Navigation.
\subsection{Übernommene Annahmen}
Ein Zahlenwert für die maximale Seitengröße war aus den bestehenden Houston-Seiten übernommen worden (NFA-4). \emph{Robin Noack} wies darauf hin, dass dieser Wert dort aus einer Beschränkung des ITSM-Systems stammt und für den Speicher keine Bedeutung hat. Was wie eine Konvention aussah, war eine technische Grenze eines anderen Systems. Der Wert wurde angepasst.
Diskutiert wurde auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte, da ein sehr großer Wert aus der Adresszeile eine entsprechend große Antwort erzwingen könnte.
Die Paginierung folgt der Arbeitsweise des Speichers, der Ergebnisse blockweise mit Fortsetzungsmerkmalen liefert \autocite{aws-listobjectsv2}. Ein direkter Sprung auf eine beliebige Seite ist nicht möglich, ohne alle vorhergehenden Blöcke abzurufen; die Akzeptanzkriterien verlangten dies auch nicht — gefordert waren Vor- und Zurück-Navigation. Ein aus den bestehenden Houston-Seiten übernommener Wert für die maximale Seitengröße (NFA-4) stammte, wie \emph{Robin Noack} anmerkte, dort aus einer Beschränkung des ITSM-Systems und wurde angepasst. Diskutiert wurde auch, ob die vom Benutzer wählbare Seitengröße nach oben begrenzt werden sollte.
\subsection{Zusammenspiel mit Freigabelinks}
Die Paginierung wirkt sich auf die Freigabelinks aus: Ein Link auf ein Dokument ist wertlos, wenn er stets auf der ersten Seite landet. Die Weiterleitung setzt deshalb nicht nur die Sprungmarke, sondern auch die Abfrageparameter für die richtige Seite. Diese Abhängigkeit war bei der Formulierung des Items berücksichtigt worden.
Ein Freigabelink auf ein Dokument ist wertlos, wenn er stets auf der ersten Seite landet. Die Weiterleitung setzt deshalb nicht nur die Sprungmarke, sondern auch die Abfrageparameter für die richtige Seite.
+5 -15
View File
@@ -3,30 +3,20 @@
\subsection{Gemeinsame Umsetzung zweier Backlog Items}
Die automatische Ordneranlage und der neue Kundenordner-Lookup wurden in einem gemeinsamen Pull Request umgesetzt. Der Lookup benötigt in seiner Rückfallebene den Fall, dass kein Ordner existiert und die Struktur angelegt werden muss. Eine getrennte Implementierung hätte einen Platzhalter erfordert, der im nächsten Pull Request entfernt worden wäre. Die Zusammenlegung wurde begründet und beide Items verknüpft.
Die automatische Ordneranlage und der neue Kundenordner-Lookup wurden in einem gemeinsamen, begründet zusammengelegten Pull Request umgesetzt, da der Lookup in seiner Rückfallebene den Fall benötigt, dass kein Ordner existiert und die Struktur angelegt werden muss.
\subsection{Normalisierung des Firmennamens}
Der erwartete Ordnername entsteht aus dem Firmennamen des Benutzers. Da dieser aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er normalisiert: umschließende Leerzeichen entfernt, strukturell bedeutsame oder nicht darstellbare Zeichen ersetzt, führende und abschließende Punkte verhindert, die Länge begrenzt.
Zwei Eigenschaften sind entscheidend. Die Normalisierung ist \textbf{deterministisch} — derselbe Eingabename ergibt stets denselben Ordnernamen, Voraussetzung für den Direktzugriff. Sie erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird.
Im Review fragte \emph{Robin Noack}, ob die Einschränkungen ausreichen; die Zulässigkeit wurde anhand der Herstellerdokumentation bestätigt \autocite{ibm-s3-naming, aws-s3-naming}.
Der erwartete Ordnername entsteht aus dem Firmennamen des Benutzers. Da dieser aus einem Fremdsystem stammt und beliebige Zeichen enthalten kann, wird er normalisiert: umschließende Leerzeichen entfernt, strukturell bedeutsame oder nicht darstellbare Zeichen ersetzt, führende und abschließende Punkte verhindert, die Länge begrenzt. Die Normalisierung ist \textbf{deterministisch} — Voraussetzung für den Direktzugriff — und erhält \textbf{Umlaute}, weil der Ordner in Filestash von Menschen gelesen wird. Im Review fragte \emph{Robin Noack}, ob die Einschränkungen ausreichen; die Zulässigkeit wurde anhand der Herstellerdokumentation bestätigt \autocite{ibm-s3-naming, aws-s3-naming}.
\subsection{Umsetzung des Lookups}
Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode zusammengefasst — der einzigen Stelle, an der ein Kundenpräfix entsteht. Der erste Schritt fragt die Metadaten des erwarteten Markers ab. Der zweite wiederholt dies mit dem um die Organisations-ID ergänzten Namen (Kollisionsfall). Erst der dritte greift auf das lineare Verfahren zurück.
Beim Prüfen, ob ein Zielname frei ist, wird nicht nur der Marker betrachtet, sondern auch geprüft, ob bereits Objekte unterhalb des Zielpräfixes liegen. Ein Zielname gilt nur als frei, wenn weder ein fremder Marker noch Inhalte vorhanden sind.
Die in Abschnitt~\ref{sec:lookup-decision} beschriebene dreistufige Auflösung wurde in einer Methode zusammengefasst — der einzigen Stelle, an der ein Kundenpräfix entsteht. Der erste Schritt fragt die Metadaten des erwarteten Markers ab, der zweite wiederholt dies mit dem um die Organisations-ID ergänzten Namen (Kollisionsfall), erst der dritte greift auf das lineare Verfahren zurück. Beim Prüfen, ob ein Zielname frei ist, wird zusätzlich geprüft, ob bereits Objekte unterhalb des Zielpräfixes liegen; ein Zielname gilt nur als frei, wenn weder ein fremder Marker noch Inhalte vorhanden sind.
\subsection{Umbenennen als Kopiervorgang}
Der Speicher kennt keine Umbenennung. Ein Objekt lässt sich nur kopieren und anschließend löschen. Eine Umbenennung ist daher nicht unteilbar — sie kann abbrechen, und Objekte liegen teils am alten, teils am neuen Ort.
Umgesetzt wurde dies über zwei Maßnahmen. Erstens wird der \textbf{Marker zuletzt kopiert}: Solange er fehlt, wird das Ziel von einer parallelen Anfrage nicht als Kundenordner erkannt. Zweitens entfernt die Fehlerbehandlung bereits erzeugte Teilkopien, damit der Vorgang wiederholbar ist.
Die zweite Maßnahme gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt. Dieser Fall ist Gegenstand des folgenden Abschnitts.
Der Speicher kennt keine Umbenennung; ein Objekt lässt sich nur kopieren und anschließend löschen. Eine Umbenennung ist daher nicht unteilbar — sie kann abbrechen, und Objekte liegen teils am alten, teils am neuen Ort. Umgesetzt wurde dies über zwei Maßnahmen: Der \textbf{Marker wird zuletzt kopiert}, sodass das Ziel von einer parallelen Anfrage nicht als Kundenordner erkannt wird, solange er fehlt; und die Fehlerbehandlung entfernt bereits erzeugte Teilkopien, damit der Vorgang wiederholbar ist. Die zweite Maßnahme gilt nur, solange kein zweiter Vorgang dieselbe Umbenennung ausführt — Gegenstand des folgenden Abschnitts.
\subsection{Auslieferung}
Der zugehörige Pull Request wurde am 26.~August zusammengeführt, nachdem \emph{Robin Noack} zwei Anmerkungen eingebracht hatte: zur Ausreichung der Namensbeschränkungen und zu der Frage, ob ein Ordner vor seiner Erzeugung auf Fremdbesitz geprüft wird. Letztere ließ sich über die Zustände des Ordnermodells beantworten — erzeugt wird nur für einen Schlüssel im Zustand \emph{fehlend}. Mit der Produktivsetzung am 3.~September ist der Lookup mit konstanter Aufrufzahl wirksam; das lineare Verfahren dient nur noch als Rückfallebene.
Der zugehörige Pull Request wurde am 26.~August zusammengeführt, nachdem \emph{Robin Noack} zwei Anmerkungen eingebracht hatte: zur Ausreichung der Namensbeschränkungen und zu der Frage, ob ein Ordner vor seiner Erzeugung auf Fremdbesitz geprüft wird. Letztere ließ sich über die Zustände des Ordnermodells beantworten — erzeugt wird nur für einen Schlüssel im Zustand \emph{fehlend}. Mit der Produktivsetzung am 3.~September ist der Lookup mit konstanter Aufrufzahl wirksam; das lineare Verfahren dient nur noch als Rückfallebene.
+5 -17
View File
@@ -3,30 +3,18 @@
\subsection{PDF-Vorschau im Modal}
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt (Abbildung~\ref{fig:shot-pdf-preview} im Anhang). Für Nicht-PDF-Dateien wird keine Vorschau angeboten.
Eine Prüfung der Dateigröße wurde bewusst nicht umgesetzt. Diese Abgrenzung wurde im Approval-Termin festgelegt: Jede Grenze wäre willkürlich, und der Browser stellt PDF-Dokumente ohnehin fortlaufend dar.
Die Vorschau öffnet PDF-Dokumente in einem überlagerten Fenster über dieselbe zeitlich begrenzte Zugriffs-URL, die auch dem Download zugrunde liegt (Abbildung~\ref{fig:shot-pdf-preview} im Anhang); für Nicht-PDF-Dateien wird keine Vorschau angeboten. Eine Prüfung der Dateigröße wurde im Approval-Termin bewusst verworfen, da jede Grenze willkürlich wäre und der Browser PDF-Dokumente ohnehin fortlaufend darstellt.
\subsection{Aufbau der Freigabelinks}
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet durch die Adresse zu transportieren.
Dass der Kundenordner nicht Bestandteil des Links ist (Listing~\ref{lst:document-id}), hat eine Wirkung: Der Link enthält keinen Hinweis auf die Organisation und ist ohne Anmeldung nicht auflösbar.
Die Seite zeigt keinen Dokumentinhalt an. Sie liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
Ein Freigabelink verweist auf eine eigene Seite, deren Adresse den relativen Pfad des Dokuments kodiert enthält. Kodiert wird nur der Anteil unterhalb des Kundenordners, um Pfadtrennzeichen und Sonderzeichen unbeschadet zu transportieren. Dass der Kundenordner nicht Bestandteil des Links ist (Listing~\ref{lst:document-id}), bewirkt, dass der Link keinen Hinweis auf die Organisation enthält und ohne Anmeldung nicht auflösbar ist. Die Seite zeigt keinen Dokumentinhalt, sondern liefert Metaangaben für die Vorschau und leitet auf die Dokumentenliste weiter, wobei Sprungmarke und passende Seite angesteuert werden.
\subsection{Vorschau in Messengern}
Der Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Abbildung~\ref{fig:shot-share-teams} im Anhang zeigt das Ergebnis in Microsoft Teams; Abbildung~\ref{fig:shot-share-target} das Ziel des Links in Houston.
Die Typsymbole liegen als Vektorgrafiken vor, die von Vorschaudiensten gängiger Messenger nicht zuverlässig dargestellt werden. Sie mussten daher zusätzlich als Rastergrafiken bereitgestellt werden. Im Pull Request wurde vermerkt, dass die Rastergrafiken bei einer Gestaltungsänderung nachzuziehen sind.
Zweck der Freigabeseite ist, dass ein in einem Messenger geteilter Link mit Titel und Symbol dargestellt wird. Dazu werden Metaangaben ausgeliefert: Dokumentname als Titel, Typsymbol als Bild, Adresse der Dokumentenliste als Ziel. Abbildung~\ref{fig:shot-share-teams} im Anhang zeigt das Ergebnis in Microsoft Teams, Abbildung~\ref{fig:shot-share-target} das Ziel in Houston. Da die Typsymbole als Vektorgrafiken vorliegen, die Vorschaudienste gängiger Messenger nicht zuverlässig darstellen, mussten sie zusätzlich als Rastergrafiken bereitgestellt werden; im Pull Request wurde vermerkt, dass diese bei einer Gestaltungsänderung nachzuziehen sind.
\subsection{Sichtbarkeit der Vorschaubilder}
Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Der Freigabe-Endpunkt ist deshalb anonym erreichbar — und damit die einzige Stelle des Moduls ohne Autorisierung.
Damit ein Messenger eine Vorschau erzeugen kann, muss er das Bild ohne Anmeldung abrufen können. Der Freigabe-Endpunkt ist deshalb anonym erreichbar — die einzige Stelle des Moduls ohne Autorisierung. Entschärft wird das durch den Aufbau der Dokumentkennung: Sie ist die Base64-Kodierung des relativen Pfades und trägt bereits alles, was die Seite ausgeben muss — Dokumentname als Titel, Dokumententyp und daraus den Pfad des Vorschaubildes sowie die Zieladresse (Listing~\ref{lst:share-page}). Der anonyme Endpunkt löst folglich keinen Aufruf an den Objektspeicher aus; er kann weder die Existenz eines Dokuments bestätigen noch Inhalte preisgeben noch als Hebel für Last dienen.
Entschärft wird das durch den Aufbau der Dokumentkennung: Sie ist die Base64-Kodierung des relativen Pfades und trägt damit bereits alles, was die Seite ausgeben muss. Aus dem dekodierten Pfad ergeben sich Dokumentname als Titel, Dokumententyp und daraus der Pfad des Vorschaubildes sowie die Zieladresse in der Dokumentenliste (Listing~\ref{lst:share-page}). Der anonyme Endpunkt löst folglich keinen einzigen Aufruf an den Objektspeicher aus. Er kann weder die Existenz eines Dokuments bestätigen noch Inhalte preisgeben, und er ist auch nicht als Hebel geeignet, um über wiederholte Aufrufe Last auf dem Speicher zu erzeugen.
Bei den sieben festen Typsymbolen ist die freie Abrufbarkeit unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden, und die genannte Eigenschaft ginge verloren.
Die Entscheidung wurde im Pull Request festgehalten. Sie zeigt, dass eine harmlose Funktion in Verbindung mit einer anderen eine Sicherheitsfrage aufwirft, die keine der beiden für sich genommen aufgeworfen hätte.
Bei den sieben festen Typsymbolen ist die freie Abrufbarkeit unbedenklich, da sie keine kundenbezogene Information enthalten. Benutzerdefinierte Symbole aus URL-Dateien werden daher \emph{nicht} für die Vorschau ausgeliefert — andernfalls müsste Dateiinhalt über einen nicht authentifizierten Pfad zugänglich gemacht werden. Die Entscheidung wurde im Pull Request festgehalten.
+5 -11
View File
@@ -3,9 +3,7 @@
\subsection{Ausgangslage}
Der Lesepfad des Kundenordner-Lookups ist unkritisch. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an, im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, reicht eine Sperre innerhalb einer Instanz nicht aus.
Bei der Durchsicht des Codes wurden zwei Fehlerszenarien gefunden, die ausdrücklich formulierte Akzeptanzkriterien verletzen.
Der Lesepfad des Kundenordner-Lookups ist unkritisch. Der in Abschnitt~\ref{sec:folder-management} beschriebene dritte Schritt ist jedoch \emph{verändernd}: Er benennt Ordner um und legt sie an, im Rahmen einer gewöhnlichen Seitenanfrage. Da Houston in mehreren Instanzen betrieben wird, reicht eine Sperre innerhalb einer Instanz nicht aus. Bei der Durchsicht des Codes wurden zwei Fehlerszenarien gefunden, die ausdrücklich formulierte Akzeptanzkriterien verletzen.
\subsection{Zwei Szenarien}
@@ -37,11 +35,11 @@ Beide arbeiten im selben Ordner; zwischenzeitlich Abgelegtes bleibt im fremden O
\label{tab:race-conditions}
\end{table}
Nicht betroffen sind zwei gleichzeitige Anfragen derselben Organisation (identische Schlüssel) sowie Lesezugriffe während einer Verschiebung, da der Marker zuletzt kopiert wird und ein halb gefülltes Ziel so nicht als Kundenordner erkannt wird. Das Problem beschränkt sich auf zwei \emph{unterschiedlich weit fortgeschrittene} verändernde Vorgänge.
Nicht betroffen sind zwei gleichzeitige Anfragen derselben Organisation (identische Schlüssel) sowie Lesezugriffe während einer Verschiebung, da der Marker zuletzt kopiert wird. Das Problem beschränkt sich auf zwei \emph{unterschiedlich weit fortgeschrittene} verändernde Vorgänge.
\subsection{Erwogene Gegenmaßnahmen}
Tabelle~\ref{tab:race-countermeasures} stellt die vier erwogenen Ansätze gegenüber. Der erste sollte unabhängig von der Serialisierungslösung umgesetzt werden; der vierte ist bemerkenswert, da er eine organisatorische statt technische Lösung darstellt.
Tabelle~\ref{tab:race-countermeasures} stellt die vier erwogenen Ansätze gegenüber; der erste sollte unabhängig von der Serialisierungslösung umgesetzt werden, der vierte ist eine organisatorische statt technische Lösung.
\begin{table}[H]
\centering
@@ -64,10 +62,6 @@ Verlagerung aus dem Anfragepfad & Einmalige kontrollierte Migration der Bestands
\subsection{Umgang mit dem Befund}
Der Befund wurde als eigenes Backlog Item mit erhöhter Priorität erfasst und vom laufenden Pull Request abgegrenzt. Das Team versah es mit einem Zeitrahmen von vier bis sechs Stunden.
Der Befund wurde als eigenes Backlog Item mit erhöhter Priorität erfasst, vom laufenden Pull Request abgegrenzt und mit einem Zeitrahmen von vier bis sechs Stunden versehen.
Den Pull Request offenzuhalten, bis die Nebenläufigkeit gelöst ist, hätte den Lookup blockiert: Ob bedingtes Schreiben verfügbar ist, ließ sich ohne Rückfrage beim Betreiber nicht beantworten, und deren Bearbeitungsdauer war zuvor mit mehreren Wochen bemessen worden. Der Lookup verbessert den Zustand gegenüber vorher bereits deutlich.
Hinzu kommt, dass der verändernde Pfad nur beim ersten Zugriff einer Organisation durchlaufen wird. Das Risikofenster besteht pro Kunde genau einmal.
Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz nicht als Lösung akzeptiert wird und dass beide Szenarien durch Unit-Tests nachzubilden sind.
Den Pull Request offenzuhalten, hätte den Lookup blockiert: Ob bedingtes Schreiben verfügbar ist, ließ sich ohne Rückfrage beim Betreiber nicht beantworten, deren Bearbeitungsdauer zuvor mit mehreren Wochen bemessen worden war. Der Lookup verbessert den Zustand bereits deutlich, und der verändernde Pfad wird nur beim ersten Zugriff einer Organisation durchlaufen — das Risikofenster besteht pro Kunde genau einmal. Die Akzeptanzkriterien des Folgeitems halten fest, dass eine reine Sperre innerhalb einer Instanz nicht akzeptiert wird und beide Szenarien durch Unit-Tests nachzubilden sind.
+3 -9
View File
@@ -3,15 +3,13 @@
\subsection{Zugriff über das AWS SDK}
Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über eine abweichende Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten. Eine eigene Implementierung — insbesondere der Signaturberechnung — wäre aufwendig und fehleranfällig gewesen.
Der Zugriff auf den Speicher erfolgt über das AWS SDK für .NET. Obwohl der Speicher nicht bei Amazon betrieben wird, ist dies der naheliegende Weg: StorageGRID implementiert die S3-Schnittstelle, und das SDK lässt sich über eine abweichende Dienstadresse auf einen beliebigen kompatiblen Endpunkt richten; eine eigene Implementierung der Signaturberechnung wäre aufwendig und fehleranfällig gewesen.
Der \texttt{S3DocumentsClient} kapselt die verwendeten Operationen: Auflisten, Metadatenabruf, Erzeugen zeitlich begrenzter Zugriffs-URLs, Lesen von Objektinhalten sowie Anlegen, Kopieren und Löschen von Objekten. Diese Kapselung hält die SDK-spezifischen Typen aus der fachlichen Schicht und ermöglicht in Tests ein Mock (siehe Abschnitt~\ref{sec:unit-tests}).
\subsection{Konfiguration mehrerer Speicher}
Houston verwendete bereits vor diesem Projekt einen S3-Speicher für das Dokumentationssystem. Mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger Speicher hinzu.
In der ersten Fassung wurden die Einstellungen als eigenständiger Konfigurationssatz geführt. Im Review wurde angeregt, eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}. Ein dritter Speicher erfordert so lediglich einen weiteren Konfigurationsabschnitt und eine Registrierung, nicht aber eine Verdopplung der Einstellungsklassen.
Houston verwendete bereits vor diesem Projekt einen S3-Speicher für das Dokumentationssystem; mit dem Dokumentenbereich kam ein zweiter, davon unabhängiger hinzu. In der ersten Fassung wurden die Einstellungen als eigenständiger Konfigurationssatz geführt. Im Review wurde angeregt, eine gemeinsame Struktur für S3-Einstellungen zu verwenden und die Verwendungen über benannte Registrierungen im Dienstcontainer auseinanderzuhalten \autocite{dotnet-keyed-di}. Ein dritter Speicher erfordert so lediglich einen weiteren Konfigurationsabschnitt und eine Registrierung.
\subsection{Umgebungen und Zugangsdaten}
@@ -19,8 +17,4 @@ Für die drei Umgebungen existiert je ein eigener Bucket mit getrennten Zugangsd
\subsection{Auflisten von Objekten}
Die zentrale Leseoperation listet Objekte unterhalb eines Präfixes auf. Sie wird mit dem Kundenpräfix aufgerufen und liefert sämtliche Objekte über alle Typordner hinweg.
Zwei Eigenschaften prägten die Umsetzung. Erstens liefert die Operation Ergebnisse blockweise: Überschreitet die Trefferzahl eine Grenze, wird ein Fortsetzungsmerkmal zurückgegeben \autocite{aws-listobjectsv2}. Diese Eigenschaft wurde für die Paginierung genutzt (siehe Abschnitt~\ref{sec:filter-pagination}). Zweitens liefert sie keine benutzerdefinierten Metadaten — die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems.
Beim Auflisten werden Platzhalterobjekte der Typordner und der Marker des Kundenordners herausgefiltert. Beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
Die zentrale Leseoperation listet Objekte unterhalb des Kundenpräfixes über alle Typordner hinweg auf. Zwei Eigenschaften prägten die Umsetzung: Erstens liefert sie Ergebnisse blockweise mit einem Fortsetzungsmerkmal \autocite{aws-listobjectsv2}, das für die Paginierung genutzt wurde (siehe Abschnitt~\ref{sec:filter-pagination}); zweitens liefert sie keine benutzerdefinierten Metadaten — die Ursache des in Abschnitt~\ref{sec:lookup-research} beschriebenen Lookup-Problems. Platzhalterobjekte der Typordner und der Marker des Kundenordners werden herausgefiltert; beide erkennt der Dienst daran, dass ihr Schlüssel auf das Trennzeichen endet.
+3 -9
View File
@@ -7,16 +7,10 @@ Die Suche filtert die Dokumentenliste anhand des Namens und berücksichtigt Teil
\subsection{Der Begriff „serverseitig"}
Die Anforderung verlangt eine serverseitige Suche. Gemeint ist, dass die Filterung in der Anwendung stattfindet, nicht im Browser — der Browser erhält die bereits gefilterte Menge. Eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden, was mit der Paginierung unvereinbar wäre.
Die Anforderung verlangt eine serverseitige Suche. Gemeint ist, dass die Filterung in der Anwendung stattfindet, nicht im Browser — eine Filterung im Browser würde voraussetzen, dass sämtliche Dokumente zuvor übertragen wurden, was mit der Paginierung unvereinbar wäre. Nicht umgesetzt ist eine Filterung durch den \emph{Speicher}: Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte \emph{Hanna Ebner} die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.
Nicht umgesetzt ist eine Filterung durch den \emph{Speicher}. Houston listet die Objekte auf und filtert die Namen anschließend selbst. Im Review stellte \emph{Hanna Ebner} die Frage, ob sich das nicht direkt über die Schnittstelle lösen lasse, und musste verneint werden.
\subsection{Warum keine Filterung im Speicher möglich war}
Im weiteren Verlauf brachte \emph{Timo Walter} die Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Objektinhalte filtert, nicht Objektnamen — sie wählt aus strukturierten Dateien einzelne Datensätze aus \autocite{aws-s3-select}.
Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen \autocite{storagegrid-search-integration}. Dessen Verfügbarkeit stand noch nicht fest; die Anfrage lief bereits (siehe Abschnitt~\ref{sec:infrastructure}). Die spätere Antwort fiel negativ aus. Auf Anregung des Reviewers wurde dieser Umstand in das Recherche-Item aufgenommen.
Im weiteren Verlauf brachte \emph{Timo Walter} die Abfragefunktion des Speichers ins Spiel. Die Prüfung ergab, dass diese Objektinhalte filtert, nicht Objektnamen — sie wählt aus strukturierten Dateien einzelne Datensätze aus \autocite{aws-s3-select}. Als zweite Möglichkeit wurde der Suchdienst des Speicherherstellers erwogen \autocite{storagegrid-search-integration}; dessen Verfügbarkeit stand noch nicht fest, die Anfrage lief bereits (siehe Abschnitt~\ref{sec:infrastructure}) und fiel später negativ aus. Auf Anregung des Reviewers wurde dieser Umstand in das Recherche-Item aufgenommen.
\subsection{Bewertung}
Die praktische Auswirkung ist derzeit gering. Die Dokumentenzahl je Kunde ist überschaubar; die Suche skaliert nur mit der Dokumentenzahl eines einzelnen Kunden, nicht mit der Kundenzahl. Der Lookup wurde daher im Projektzeitraum gelöst, die Suchoptimierung blieb für den Ausblick.
Die praktische Auswirkung ist derzeit gering, da die Suche nur mit der Dokumentenzahl eines einzelnen Kunden skaliert, nicht mit der Kundenzahl. Der Lookup wurde daher im Projektzeitraum gelöst, die Suchoptimierung blieb für den Ausblick.
+3 -7
View File
@@ -3,16 +3,12 @@
\subsection{Eigene Symbole statt Symbolschrift}
Houston verwendet für Symbole eine gängige Symbolbibliothek. Für die sieben Dokumententypen enthielt diese keine passenden Motive — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt.
Die Entwürfe stammen von mir und wurden in Abstimmung mit \emph{Hanna Ebner} ausgearbeitet, damit sie sich in Strichstärke, Abmessung und Bildsprache in die vorhandene Symbolbibliothek einfügen. Alle Symbole greifen dieselbe Dokumentkontur auf und unterscheiden sich nur im Innenmotiv, sodass die Typzugehörigkeit auf einen Blick erkennbar ist, ohne dass die Zeilen unruhig wirken. Abbildung~\ref{fig:type-icons} im Anhang zeigt den vollständigen Satz.
Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet. Houston unterstützt jedoch ein helles und ein dunkles Erscheinungsbild, und fest eingefärbte Symbole wären im dunklen kaum sichtbar gewesen.
Houston verwendet für Symbole eine gängige Symbolbibliothek, die für die sieben Dokumententypen keine passenden Motive enthielt — Begriffe wie „SLA-Report Ticketbearbeitung" lassen sich mit allgemeinen Symbolen nicht unterscheiden. Es wurden daher eigene Vektorgrafiken erstellt. Die Entwürfe stammen von mir und wurden in Abstimmung mit \emph{Hanna Ebner} ausgearbeitet, damit sie sich in Strichstärke, Abmessung und Bildsprache in die vorhandene Symbolbibliothek einfügen; alle greifen dieselbe Dokumentkontur auf und unterscheiden sich nur im Innenmotiv, sodass die Typzugehörigkeit auf einen Blick erkennbar ist (Abbildung~\ref{fig:type-icons} im Anhang).
\subsection{Einfärbung über Masken}
Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfläche dienen. Die Fläche übernimmt die aktuelle Textfarbe, sodass sich das Symbol automatisch anpasst. Die eigenen Symbole lassen sich über dieselben Gestaltungsklassen ansprechen wie die vorhandenen. Alle Grafiken müssen dieselben Abmessungen haben; im Review fiel auf, dass eine abwich, und die Maße wurden angeglichen.
Eine Einbindung als gewöhnliche Grafik hätte eine feste Farbe bedeutet, die im dunklen Erscheinungsbild von Houston kaum sichtbar gewesen wäre. Gelöst wurde dies, indem die Vektorgrafiken als Maske über einer Hintergrundfläche dienen, die die aktuelle Textfarbe übernimmt; die eigenen Symbole lassen sich so über dieselben Gestaltungsklassen ansprechen wie die vorhandenen. Alle Grafiken müssen dieselben Abmessungen haben; im Review fiel auf, dass eine abwich, und die Maße wurden angeglichen.
\subsection{Standardsymbol}
Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol (letzter Eintrag in Abbildung~\ref{fig:type-icons}). Damit trägt jede Zeile ein Symbol, und an der Darstellung ist erkennbar, dass keine Typzuordnung vorliegt, ohne dass dies wie ein Fehler wirkt.
Dokumente ohne erkennbaren Typ erhalten ein neutrales Standardsymbol (letzter Eintrag in Abbildung~\ref{fig:type-icons}). Damit trägt jede Zeile ein Symbol, und die fehlende Typzuordnung ist erkennbar, ohne wie ein Fehler zu wirken.
+6 -20
View File
@@ -3,32 +3,18 @@
\subsection{Anwendungsfall}
Nicht alle Unterlagen lassen sich sinnvoll in den Speicher kopieren. Ein fortlaufend gepflegtes Dokument oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt werden — eine Kopie wäre sofort veraltet. Dafür werden \texttt{.url}-Dateien unterstützt: kleine Textdateien im INI-Format mit Zieladresse und optionalem Symbol. In der Liste erscheinen sie wie gewöhnliche Dokumente, führen beim Anklicken jedoch auf die hinterlegte Adresse.
Nicht alle Unterlagen lassen sich sinnvoll in den Speicher kopieren; ein fortlaufend gepflegtes Dokument oder ein Kanal in einem Kollaborationswerkzeug soll verlinkt werden, da eine Kopie sofort veraltet wäre. Dafür werden \texttt{.url}-Dateien unterstützt: kleine Textdateien im INI-Format mit Zieladresse und optionalem Symbol, die in der Liste wie gewöhnliche Dokumente erscheinen, beim Anklicken jedoch auf die hinterlegte Adresse führen. Der Anwendungsfall wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}).
Der Anwendungsfall wurde in der Feature-Analyse geklärt: Gemeint ist das Einbetten \emph{externer} Ressourcen, nicht das Teilen von Houston-Dokumenten nach außen (siehe Abschnitt~\ref{sec:feature-analysis}).
\subsection{Auswertung und Anzeigename}
\subsection{Auswertung des Dateiformats}
Für das INI-Format wurde bewusst keine externe Bibliothek eingebunden. Benötigt wird ein einziger Wert aus einem Abschnitt. Eine Bibliothek brächte eine dauerhaft zu pflegende Abhängigkeit für Funktionalität, die in wenigen Zeilen selbst geschrieben ist. Zudem würde sie bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist.
\subsection{Rückfall auf normales Verhalten}
Kann keine gültige Adresse erkannt werden, wird die Datei wie eine gewöhnliche Datei zum Herunterladen angeboten. Eine fehlerhafte Verknüpfungsdatei darf das Dokument nicht unzugänglich machen.
\subsection{Anzeigename}
Die Endung \texttt{.url} wird für die Anzeige entfernt. Im Review wies \emph{Robin Noack} darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand; stattdessen sollte die Länge der Endung selbst verwendet werden, da Zahl und Endung an verschiedenen Stellen stehen und bei einer Änderung auseinanderlaufen können.
Für das INI-Format wurde bewusst keine externe Bibliothek eingebunden: Benötigt wird ein einziger Wert aus einem Abschnitt, der in wenigen Zeilen selbst gelesen ist, und eine Bibliothek würde bei fehlerhaften Dateien vermutlich einen Fehler melden, während hier ein stiller Rückfall erforderlich ist. Kann keine gültige Adresse erkannt werden, wird die Datei wie eine gewöhnliche Datei zum Herunterladen angeboten. Die Endung \texttt{.url} wird für die Anzeige entfernt; \emph{Robin Noack} wies im Review darauf hin, dass die dafür verwendete Zeichenzahl als unmittelbare Zahl im Code stand und stattdessen die Länge der Endung selbst verwendet werden sollte, da beide sonst bei einer Änderung auseinanderlaufen.
\subsection{Symbole für Verknüpfungen}
Das Dateiformat sieht im Abschnitt \texttt{[InternetShortcut]} einen Eintrag \texttt{IconFile} vor, der unter Windows auf eine Symboldatei zeigt \autocite{nsis-shortcuts}. Ein Verweis auf eine Datei ist im Browser nicht verwertbar, und aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen werden benutzerdefinierte Symbole ohnehin nicht ausgeliefert. Der Eintrag wird daher umgedeutet: Er trägt keinen Dateipfad, sondern den Namen eines Symbols, das die Anwendung bereits kennt.
Das Dateiformat sieht im Abschnitt \texttt{[InternetShortcut]} einen Eintrag \texttt{IconFile} vor, der unter Windows auf eine Symboldatei zeigt \autocite{nsis-shortcuts}. Ein Dateipfad ist im Browser nicht verwertbar, und aus den in Abschnitt~\ref{sec:pdf-preview} genannten Gründen werden benutzerdefinierte Symbole ohnehin nicht ausgeliefert. Der Eintrag wird daher umgedeutet: Er trägt den Namen eines Symbols, das die Anwendung bereits kennt. Zulässig sind die Klassen der eigenen Typsymbole, etwa \texttt{doc-type-icon-vertragsunterlagen}, sowie Klassen der in Houston vorhandenen Symbolbibliothek Boxicons, etwa \texttt{bx bxl-github} \autocite{boxicons}. Für die drei häufigsten Verknüpfungsziele — Microsoft Teams, SharePoint und OneDrive — wurden im selben Stil eigene Grafiken ergänzt (Abbildung~\ref{fig:type-icons} im Anhang).
Zwei Formen sind zulässig. Zum einen die Klassen der eigenen Typsymbole, etwa \texttt{doc-type-icon-vertragsunterlagen}; zum anderen eine Klasse der in Houston vorhandenen Symbolbibliothek Boxicons, etwa \texttt{bx bxl-github} \autocite{boxicons}. Für die drei häufigsten Verknüpfungsziele — Microsoft Teams, SharePoint und OneDrive — wurden im selben Stil wie die Typsymbole eigene Grafiken ergänzt (Abbildung~\ref{fig:type-icons} im Anhang).
Fehlt der Eintrag oder ist er leer, greift dieselbe Regel wie für gewöhnliche Dateien: Liegt die Verknüpfung in einem bekannten Typordner, erscheint dessen Symbol, sonst das Standardsymbol. Ein unbekannter Wert führt damit nie zu einer leeren Zelle. Abbildung~\ref{fig:shot-url-icons} im Anhang zeigt die Fälle nebeneinander.
Da die Kundenbetreuer diese Dateien von Hand anlegen, wurden Format, zulässige Werte und Rückfallverhalten im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
Fehlt der Eintrag oder ist er leer, greift dieselbe Regel wie für gewöhnliche Dateien: Liegt die Verknüpfung in einem bekannten Typordner, erscheint dessen Symbol, sonst das Standardsymbol; ein unbekannter Wert führt damit nie zu einer leeren Zelle (Abbildung~\ref{fig:shot-url-icons} im Anhang). Da die Kundenbetreuer diese Dateien von Hand anlegen, wurden Format, zulässige Werte und Rückfallverhalten im internen Wiki dokumentiert und im Pull Request darauf verwiesen.
\subsection{Ausschluss vom ZIP-Download}
Verknüpfungsdateien erhalten keine Auswahlbox und können nicht Teil eines Archivs werden. Ein Archiv enthielte sonst eine Datei, die beim Öffnen eine möglicherweise nur intern auflösbare Verknüpfung liefert. Da eine Verknüpfung fachlich kein Dokument ist, wäre ihre Aufnahme irreführend.
Verknüpfungsdateien erhalten keine Auswahlbox und können nicht Teil eines Archivs werden, das sonst eine Datei mit einer möglicherweise nur intern auflösbaren Verknüpfung enthielte. Da eine Verknüpfung fachlich kein Dokument ist, wäre ihre Aufnahme irreführend.