2021-04-20 Fass ohne Boden

Was haben wir die letzten Tage gemacht? Wo fangen wir an?

Vielleicht auf der UI-Seite
Schon im letzten Sprint haben wir Coding aufgeräumt und Schnittstellen standardisiert. Damit haben wir auch diese Tage weitergemacht. Spez. bei der Verwendung von Images. In der App werden bisher zwei Formate verwendet, um den Zugriff auf Images zu ermöglichen.

  • Einerseits Uri-Objekte, die auf ein Image verweisen
  • Andererseits ein Objekt vom Typ IImage, das wir eingeführt haben

IImage besitzt ein Property ImageSource, womit auf das Bild selbst zugegriffen werden kann. Die ImageSource kann entweder ein Bitmap, eine Android Resource oder ein Uri-Objekt sein.
Da ImageSource das erste Format – Objekte vom Typ Uri – unterstützt, haben wir nun jeglichen Zugriff auf unser IImage umgestellt.

Soweit so gut.

Ein zweites größeres Thema im Sprint waren Unittests, die wir etwas weiter forcieren wollen. Bspw. auch für Änderungen an Klassen für die Verarbeitung von Bildern, die wir nun umgestellt haben. Und voila es hat nicht alles funktioniert :D.
Trotz Einsatz von Mockito konnten nicht alle Fälle zum Testen abgedeckt werden. Denn bei Verwendung von Uri-Objekten haben wir auf statische Funktionen (Uri.Parse()) zugegriffen, die in den zu testenden Klassen aufgerufen wurden. Die Uri-Klasse hat sogar noch eine Fehlermeldung aller: „ist beim Testen zu mocken“, bei der Ausführung der betroffenen Tests ausgegeben.
Außerdem verwenden wir Extensions (String.toUri()), die wir nicht durch andere Aufrufe ersetzen wollen.
Also musste eine andere Lösung her. Mockk sollte es sein, ein Mocking-Framework, das speziell für die Verwendung in Kotlin zugeschnitten ist. Und man muss sagen, es funktioniert großartig. Anfangs gab es ein paar Schwierigkeiten die richtige Dependency zu laden, aber letztendlich ist das Coding deutlich lesbarer als bei der Verwendung von Mockito, das Mocken von statischem Code ist möglich und auch Extensions können gemockt werden. Hier ein kleines Beispiel:

Beispiel Verwendung mockk

Vielleicht ein paar Erklärungen dazu:
Die Klasse Uri stellt die Extension „toUri“ für die Klasse String bereit. Damit können Objekte vom Typ String automatisch in eine Uri umgewandelt werden. Extensions sind statisches Coding, das bspw. mit Mockito nicht einfach getestet werden kann.
Über Mockk ist das über die Funktion „mockkStatic“ möglich. Es muss lediglich die Klasse übergeben werden, auf der die Extension implementiert ist.
In der zweiten Zeile wird ein zweites Mock-Objekt vom Typ Uri erstellt.
In der darauffolgenden Zeile wird das erwartete Verhalten des Mocks beim Aufruf der Extension „toUri“ festgelegt. Immer wenn für einen String (hier uriString) die Extension „toUri“ aufgerufen wird, soll das zuvor gemockte Objekt uri zurückgegeben werden.
Anschließend erfolgt der zu testende Funktionsaufruf und abschließend wird durch die Funktion verify überprüft, ob „toUri“ aufgerufen wurde. Die Überprüfungen können auch deutlicher vielfältiger und komplexer sein. Eine Übersicht der meisten Features findet man in der Reihe Mocking is not rocket science und ganz konkrete Details in der zugehörigen Doku.

Dank mockk konnten auch die bestehenden Unittests, die Zugriff auf den Android Context benötigen, umgezogen werden. Vielleicht hätten wir das auch bereits im Vorfeld mit Mockito machen können. Test mit spez. Bezug zu Android wollen wir so gering wie möglich halten.

Um mit mockk etwas zu arbeiten, sind noch für ein paar zusätzliche Klassen Unittests rausgepurzelt und Dokumentationen spez. für Interfaces ergänzt worden.

Das Fass ohne Boden
Das zweite große Thema war Keycloak, OAuth2 und die Möglichkeit serverseitig ausgeloggte Benutzer den Zugriff auf Endpunkte des Webservers zu verwehren.
Bisher ist es so: Wenn ein User seinen JWT (JSON Web Token) hat, dann kann er munter auf Endpunkte des Resource-Servers zugreifen, auch wenn der User bereits in Keycloak ausgeloggt wurde.
Was uns fehlt ist generell eine Verbindung zwischen unserem Webserver (Resource-Server) und Keycloak.
Die Idee ist also zu prüfen, ob ein Token noch gültig ist. Bisher validieren wir den Token lediglich auf Webserver-Seite. Dank OAuth2 Spezifikation ist das Theme Validierung von Tokens bereits vorgesehen. Es gibt einen speziellen Endpunkt „introspect“.
Warum also nicht diesen Endpunkt in Keycloak aufrufen, den User-Token zur Prüfung übergeben und abhängig vom Ergebnis, ob der Token gültig ist oder nicht den Zugriff auf die jeweilige Resource (bspw. alle Teams auslesen) ermöglichen?
Letztendlich scheint es, als ob unser Keycloak-System dafür noch nicht konfiguriert ist. Die Tests wurden mit Postman durchgeführt, aber das Ergebnis waren Fehler wie: „invalid_client_credentials“ oder „invalid_request – Authentication failed“.
Zur Authentication kann im Grunde entweder ein Bearer-Token oder eine Basic-Authentification (User Credentials) verwendet werden. Aber weder mit dem Admin-User, noch einem aktuellen Bearer-Token und auch nicht mit Client-Id / Client Secret hat es funktioniert. Später ist aufgefallen, dass die Einstellung des Clients möglicherweise gar nicht korrekt ist.
Kurz: es scheint, als hätten wir das Fass ohne Boden wieder aufgemacht. Denn an Keycloak, OAuth und alles was damit zutun hat, haben wir schon viel Zeit verbracht.
Aber das bekommen wir auch in den Griff.

Das war es für diese Woche.

2021-04-13 Von allem etwas

Beginnen wir mit einer Beichte: Im letzten Sprint haben wir unseren Blog-Artikel nicht verfasst und ziehen ihn heute – eine Woche später – nach :P.

Was haben wir im letzten Sprint gemacht? Wir waren Hansdampf in allen Gassen. Wir haben am Backend, Frontend, an der Qualität, Developer-Support und Scrum gearbeitet. Grundsätzlich waren es eher mehr, aber dafür kleinere Aufgaben. In den folgenden Zeilen gibt es die Details.

Fangen wir mit dem Backend an
Schon über mehrere Sprints hinweg arbeiteten wir an einem kleinen Framework, um Content für die App aufzubauen. Oft war der Content zu wechselhaft, um bspw. ein spez. Feature zu testen. Ab sofort hat das ein Ende. Unsere Model-Builder haben den vorerst letzten Schliff bekommen und werden seither zum Aufbau eines Standardprojektes beim Starten des Webservers eingesetzt. Ob User mit Achievements oder Teams mit User inkl. Achievements. Nach jedem Start finden wir die selbe Konstellation des Projektes vor. Sofern erforderlich können wir dieses Projekt beliebig erweitern oder durch ein komplett anders zusammengestelltes Projekt ersetzen.
Beim Testen sind wir – nicht zum ersten Mal – einem Fehler beim Starten des Webservers nachgegangen, der seinen Ursprung im fehlenden Keycloak-Server (Id-Provider) hat. Nachdem er uns schon häufiger etwas Zeit gekostet hat, haben wir unser Backend nun derart erweitert und prüfen bei jedem Start, ob Keycloak erreichbar ist. Wenn nicht, dann wird nun eine sprechende Meldung ausgegeben. Das sollte uns zukünftig Zeit und unnötige Fehleranalyse sparen.

Fehlertext nach Prüfung Erreichbarkeit des Keycloak-Servers
Fehlertext nach Prüfung Erreichbarkeit des Keycloak-Servers

Frontend
Im Frontend haben wir etwas ausgemistet. Eine unserer Fleißarbeiten war es alle Texte in die dafür vorgesehene Text-Datei auszulagern. Im Großen und Ganzen nutzen wir diese Datei auch. Aber hier und da gab es noch Probleme. Eine Schwierigkeit ist vor allem der Zugriff auf die Texte im Bereich „Resources“, denn dafür wird ein Context-Objekt benötigt, das nicht immer zur Verfügung steht. Bereits in der Vergangenheit haben wir dafür einen TextService eingeführt. Statt beim Context den Text zu requesten, kann die gleiche Anfrage über den TextService erfolgen. Nun auch durch Mitgabe von Platzhalterwerten. Indirekt haben wir so zwar immer noch eine Abhängigkeit zum Context aber könnten sie auflösen, sofern wir dafür eine geeignete Lösung finden oder die Notwendigkeit bestünde. Aktuelle geht es auch so.
Eine weitere Aufräumarbeit war die Standardisierung der Model-Klassen. Jede Model-Klasse implementiert nun das Interface IHasId und ist somit in der Lage seine Id zur Verfügung zu stellen.
Den letzten größeren Zeitblock haben wir Unittests gewidmet. Bisher haben wir davon nur wenige geschrieben und dieses Mal wenigstens ein paar nachgezogen und uns noch etwas mehr mit Mockito vertraut gemacht bzw. dran gewöhnt.

Das Thema Scrum
Schon seit längerem ist uns aufgefallen, wie unser Backlog wächst und wächst und allmählich die Übersicht verloren ging. Dem haben wir einen Riegel vorgeschoben und uns jedes Backlog Item angeschaut, Duplikate gelöscht, fehlende Details ergänzt oder doppelte konsolidiert, überflüssige Kategorien ins Jenseits befördert und größere, offene Themen in einen Bereich „Open Epics“ verschoben.

Das war der Rückblick des letzten Sprints. Die Inhalte des Folgenden sind: geheim!

2021-04-06 Happy Easter

Diese Woche sind wir feiertagsbedingt nicht sehr viel weitergekommen. Es gab nur einen Tag und das mit halber Besetzung, weshalb nur ein paar Kleinigkeiten erledigt wurden.

Vielleicht noch etwas chaotisch, aber trotzdem ist es ein anfänglicher Versuch, mit Miro ein Diagramm für unsere Team-, User und Achievement-Builder und Pools zu erstellen. Seltsamerweise wollten die Pfeilspitzen heute nicht funktionieren, weshalb es nicht ganz so lesbar ist, wie es sein könnte.

Das Szenario hat ein paar sinnvolle Achievements erhalten. Der AchievementBuilder kann nun auch ohne Zufall Achievements heraussuchen, wurde noch ein bisschen aufgeräumt und auch die Unittest-Aufgabe konnte abgehakt werden, da der Unittest für ihn auch wieder läuft und erweitert wurde.

In der restlichen Zeit wollte ich mir zuerst die App auf dem Handy anschauen, wofür der UserService-Mock erweitert werden musste, damit die App wieder ohne Backend läuft. Besonders auffällig war der mintfarbene Button, in den sich unser Floating Action Button vor einiger Zeit verwandelt hat, nachdem wir für von einem Google Material Theme geerbt haben. Hier sind jetzt nicht nur die Primary, sondern auch die Secondary-Farben gesetzt und der Button verursacht keinen Augenkrebs mehr. Dann wollte ich die Tastatur wieder ausblenden, was sich eigentlich einfach anhörte, aber ich doch als etwas komplizierter herausgestellt hat, weshalb ich es auf nächstes Mal verschoben habe.

2021-03-30 Contribution completed

Wir haben es geschafft. User können etwas zu einem Achievement contributen (party). Im letzten Sprint haben wir noch an der Anzeige der Achievements gearbeitet. In diesem haben wir uns um das Contributen selbst gekümmert. D.h. ein contributeter Wert wird nun ins Backend übertragen, gespeichert und der ProgressBar zur Anzeige des offenen Contributes im UI aktualisiert.

Contributen mehrere Personen parallel werten wir die Eingaben aus und zeigen entsprechende Meldungen an. Dafür haben wir zum ersten Mal ein DTO (Data Transfer Object) eingeführt, das lediglich aus zusammengesetzten Attributen besteht und keine eigene Entität (wie Teams, User, Achievement) darstellt. Beim Contributen wird es als Ergebnis aus dem Backend ans Frontend übertragen. Es enthält den RequestedContributedValue (was sollte beigesteuert werden?), den ActualContributedValue (was wurde tatsächlich begesteuert?) und das betroffene Achievement. Denn wurde bei einem gleichzeitigen Contribute mehrerer Benutzer das Achievement completed oder konnte nur noch ein Teil contributet werden, bekommt der Benutzer Bescheid: „Ey, während du versucht hast zu contributen, hat ein anderer Benutzer das Achievement bereits completed. Bitte contribute doch stattdessen bei einem anderen Achievement den Rest deines Contributes.“
An dieser Sharing-Funktion müssen wir allerdings noch arbeiten. Denn was ist der gemeinsame Nenner, die gemeinsame Einheit für „365 Tage im Jahr“ und „1000 Ball Catches“? 1 Ball Catch ist nicht gleich bedeutend mit einem Tag Snowboarden im Jahr.
Wir haben ebenfalls darüber gesprochen, dass ein Achievement doch eigentlich das Ergebnis eines Abschlusses einer Challenge ist. Müssten Achievements also nicht zuvor Challenges heißen? Darum müssen wir uns noch kümmern.

Ach, hier noch ein Screen von unserer Contribute-Progress-Funktion. Warum auch nicht?

Blau – bereits contributeter Wert (von einer beliebigen Anzahl von User)
Lila – contributeter Wert
Grau – noch offen zu contributender Wert

Hm, an weiteren Themen haben wir nicht groß gearbeitet :?.
Das Achievement-Bild wurde mit jedem Klick auf Plus oder Minus – um den ContributeValue zu erhöhen – neu geladen und hat zu einem unangenehmen Blink-Effekt geführt, den wir gefixt haben.
Außerdem fehlte noch die Aktualisierung der Achievement-Übersicht nach dem Contributen, denn dort wird sowohl der Contribute-Progress, als auch ein Datum eingeblendet, wenn das Achievement erreicht / die Challenge abgeschlossen wurde.

Wir haben uns zusätzlich um die Erweiterung der Builder gekümmert, die wir für das Aufbauen von Test-Szenarien beim Starten des Webservers nutzen. Bspw. wurde aus dem Pool von Achievements eines zufällig ausgewählt und einem User oder Team zugeordnet. Zu Beginn schien das eine super Erleichterung zu sein. Allerdings stellten wir fest: auch der Aufbau von immer identischen Szenarien macht Sinn, um immer wieder den selben Sachverhalt zu testen.

Und was steht demnächst an? Zu Beginn dürften dem User keine Achievements / Challenges zugeordnet sein, wie es noch der Fall ist. Stattdessen müsste sich der User für eine Challenge entscheiden und contributen. Erst danach dürfte ihm das Item in seinem Bereich angezeigt werden. Außerdem wollen wir jeden Contribute speichern. Vielleicht motiviert es manch einen, wenn wir die Möglichkeit bieten anzuzeigen, was contributet wurde.

2021-03-23 Wortschatz für Superhirne

Indolent, drakonisch, lakonisch, jovial – was haben diese Worte mit unserem Sprint zutun? Nichts :P. Also nicht direkt mit unserem Projekt sondern mit unserem Zeitvertreib. Dieser Sprint war aus zweierlei Hinsicht besonders, als das wir A in unserem Urlaub fakultativ weiter an TeamSlimster gearbeitet haben und wir B nebenbei etwas unseren Wortschatz erweitert haben. Ein Buch, um eloquent, intelligent, eingebildet, hochgestochen zu klingen oder ein Buch um einfach ein bisschen Spaß bei der Arbeit zu haben?! Wofür auch immer. In jedem Fall hat es uns etwas die Zeit beim Abfragen, Raten und Lachen versüßt.

Und was haben wir noch gemacht? Wir arbeiten noch an der Umsetzung der Achievements im Allgemeinen und am Contributen im Speziellen.
Das UI hatte wir bereits letzte Woche überarbeitet. Zu Beginn des aktuellen Sprints ging es der Stepgröße zum Contributen an den Kragen. Wie bereits im letzten Blogeintrag erwähnt, macht es keinen Sinn beim Achievement „365 Tage im Jahr“ in 10er Schritten den zu contributenden Wert zu erhöhen. Wiederum ist die Chance groß beim Achievement „1000 Ball Catches“ die Murmel mehr als einmal von Partner A zu B zu werfen. Und was passiert mit Achievements, deren Aufgabe etwa erfüllt wurde oder eben nicht?
Wir haben uns entschieden die Information ContributeProposalStepSize an die Achievement-Templates zu hängen. Beim Erzeugen eines konkreten Achievements wird der Wert an das zugehörige Progress-Objekt übergeben. In der App haben wir unsere Plus- / Minus-Buttons an die Stepsize geknüpft. Ein Tag mehr Snowboarden im Jahr oder doch 100 Ballwechsel? Voilà, jetzt geht es und ist gleich schöner.
Bei der Stepgröße konnte wir es nicht belassen. Was ist mit der Darstellung? Für ein SingleContributeAchievement (ein Achievement, das mit einmal abgeschlossen werden muss – „Blog Eintrag geschrieben? Ja oder nein“) wird nun lediglich ein Button „Done“ angezeigt. Nicht zu vergessen ist der Switch aus der Contribute-Darstellung in die Finish-Darstellung, wenn das Achievement erreicht wurde.
Bugs bei der Eingabe des ContributeValues hatten wir auch einige. Denn bei jeder Eingabe muss der ProgressPreviewBar (zur Anzeige welchen prozentualen Beitrag ein Contribut auf den gesamten Progress hat) aktualisiert werden, was wiederum indirekt eine Änderung des Eingabewertes nach sich zog, was wiederum eine Aktualisierung des ProgressPreviewBars zur Folge hatte, dessen Update wieder indirekt eine Aktualisierung des Eingabefeldes auslöste, worauf hin es sich der ProgressPreviewBar nicht nehmen ließ sich zu refreshen … Endlosschleifen sind nicht dauerhaft unterhaltsam.
Ach und die Geschichte mit dem Cursor. Beim Setzen des Wertes für das Eingabefeld des ContributValues wurde der Cursor des Eingabefeldes permanent neu positioniert. Aber auch das haben wir in den Griff bekommen. Letztendlich hat die Implementierung einer schicken Extension geholfen, die uns auch zukünftig vor diesen Problemen schützen sollte.

Haben wir schon erwähnt, dass wir über Beschreibungen unsere Test-Achievements brüteten?
Unsere drei Contribute-Funktionen sehen nur wie folgt aus:

1. Achievement mit mehrfacher Contribute Funktion
2. Achievement mit einfacher Contribute Funktion
3. Abgeschlossenes Achievement

Was gab es sonst? Vielleicht gehen wir gerade wieder etwas durch das Tal der Tränen. Wir sehen, wir kommen voran. Aber vielleicht haben wir etwas unser Ziel aus den Augen verloren? Aber das wird wieder besser.

Ausblick: Mit den Achievements sind wir immer noch nicht durch. Was in jedem Fall fehlt ist die Übertragung der contributeten Werte ins Backend.

2021-03-16 Nur mal schnell das ObtainDate hinzufügen

Diese Woche haben wir halb mit UI- und halb mit Backend-Entwicklung verbracht. Nachdem wir letzte Woche mit den Achievement-Details angefangen haben, haben diese zunächst ein Upgrade bekommen. Hier sieht man vorher/nachher:

Der Fortschritt wird jetzt kreisförmig in blau angezeigt. Man hat die Möglichkeit zu einem Achievement beizutragen. Durch das Klicken auf die Plus/Minus-Buttons oder das Eintragen einer Zahl wird der Fortschritt in lila zum Progress hinzugefügt. Die Contribute-Funktionalität haben wir uns für nächste Woche vorgenommen, da die backend-seitigen Änderungen etwas Zeit in Anspruch nehmen werden, da wir gerne eine Statistik hätten, welcher User wann wieviel beigetragen hat. Für die UI haben wir auch noch ein paar Verbesserungsideen, wie z.B. die sinnvollere Befüllung des Contribute-Wertes je nach Achievement, da es einen Unterschied macht, ob es ein Gruppenachievement ist, bei dem z.B. jeder User 20 Push-ups zur 20 000 Push-up-Challenge beiträgt oder ob man in zwei Etappen mit dem Rad von Stuttgart nach Berlin fährt und dann die 300 km zweimal einträgt. Das hat auch einen Einfluss auf die Vorbelegung der Buttons, die den Wert verändern. Ein weiteres Feature könnte sein, dass sich beim Gedrückt-Halten der Buttons der Wert ändert bis man loslässt. Für so einen kleinen Screen gibt es also noch viel Verbesserungspotenzial.

Am zweiten Tag wollten wir „mal eben“ für den Achievement-Detailscreen den obtainValue, also einen Wert, bei dem ein Achievement erreicht wurde und den tatsächlichen Erreichungswert, bei dem der User sich für das Achievement gerade befindet, im Backend anlegen. Dabei ist uns zum zweiten Mal aufgefallen, dass wir Änderungen sowohl für Teams als auch für User machen müssten. Wir waren uns bereits letztes Mal bewusst, dass wir damit einen Fuß in die Copy-Hölle gesetzt hatten, konnten allerdings vorübergehend damit leben. Führt man die Analogie weiter, mussten wir, um den sengenden Fuß zu löschen, einen Ozean umleiten. Es hat also etwas gedauert, aber dafür konnten wir ein ausgiebiges Sonnenbad in Generics genießen und jetzt ist alles an seinem, und noch viel wichtiger, an einem Platz.

2021-03-09 Vor und zurück und doch einen Schritt weiter

Diese Woche haben wir einige Sachen entwickelt und wieder verworfen. Zuerst haben wir damit begonnen, ein Autowiring für unseren Service Provider zu implementieren. Wir wollten nicht jeden Service selbst registrieren, sondern anhand des Interfaces die implementierende Klasse ermitteln und instanziieren. Nach längerem Gewühle war klar, dass diese Info nicht so einfach zu haben ist, wie wir es aus ABAP gewohnt sind. Wir wollten dann während des Builds auf den ClassLoader zugreifen, uns die Infos speichern und zur Laufzeit abholen, aber nach ein bisschen Herumprobieren mit Gradle mussten wir akzeptieren, dass wir hierfür mal wieder ein ganz neues Fass aufmachen müssen. Also haben wir es vorerst verschoben, haben es aber noch nicht gänzlich abgehakt.

Als nächstes haben wir uns mit unseren Achievements beschäftigt. Wir wollen Details zu den jeweiligen Achievements anzeigen und dabei eine Möglichkeit schaffen, zu einem Achievement beizutragen, also so etwas wie „Ich habe heute 20 Push-ups zur 10 000 Push-ups-Challenge beigetragen“. Dazu wollten wir einen Detailbereich spendieren, der erscheint, wenn man auf ein einzelnes Achievement in der Liste klickt. Die erste Idee war, dass man direkt darunter eine Kachel über die gesamte Breite anzeigt. Hierzu haben wir etwas Zeit in die Einbindung des Flexbox-Layouts von Google gesteckt, mit dessen Hilfe Kacheln unterschiedlicher Größe innerhalb der Recyclerview angezeigt werden können. Allerdings ist diese Entwicklung mit etwas mehr Aufwand verbunden und in der App, in der Peter diese Funktionalität entdeckt hatte, nicht mehr vorhanden. Für uns war das ein Zeichen, uns für eine andere Lösung zu entscheiden. Dafür haben wir zunächst die Snackbar verwendet, um die Details unten anzuzeigen. Das Layouting hat uns hier allerdings einen Strich durch die Rechnung gemacht und wir konnten sie nicht dazu bewegen, eine ausreichende Höhe anzuzeigen. Deshalb haben wir erstmal auf eine eigene View gewechselt, die wir zunächst ein- und ausblenden. Aber auch hier haben wir Probleme beim Layouting. Entweder machen wir hier etwas grob falsch, oder das Layouting für ein mehrzeiliges Textfeld ist einfach buggy. Der Progressbar, mit dem wir den Fortschritt des Achievements anzeigen wollen, erstreckt sich trotz Constraints fast über die gesamte Breite, weshalb wird das Layout nochmal überdenken wollen. Eine Idee für nächste Woche ist es, den Fortschritt als Kreis um das Image darzustellen. Mal sehen, wie das aussieht. Das würde auch besser zu unserem Fortschrittsbalken passen, mit dessen Hilfe man seinen Fortschritt teilen kann.

Alles in allem sind wir nicht sehr weit voran gekommen, aber haben wie immer sehr viel neues Wissen daraus mitgenommen.

2021-03-02 Stein auf Stein..die Beispieldatenumgebung wird bald fertig sein

Diese Woche haben wir einen Großteil unserer Zeit darin investiert, unser Backend weiter mit Beispieldaten zu füttern, die über Builder zusammenkonfiguriert werden können. Damit hatten wir bereits letzte Woche begonnen und sind so gut wie fertig. Beim Starten des Webservers werden nun User, User-Achievements, Teams und zugehörige Achievements und Member angelegt. Dies passiert bisher noch Random, da wir unsere Builder so angelegt haben, dass man z.B. eine bestimmte Anzahl an Teams anlegen kann, ohne die kompletten Informationen anzugeben. Die nötigen Informationen werden dann Random aus einer Template-Liste ausgesucht und daraus einfache Teams erzeugt. Was uns hier noch fehlt ist eine Art Szenario, welches wir uns konfigurieren wollen, um bei jedem Start sinnvolle Beispieldaten zu erhalten, also z.B. der Test-User einem Team zugewiesen ist, welches über weitere Member und Achievements verfügt. Das haben wir uns für nächste Woche gelassen. Außerdem ist Max Müller als User verschwunden und unsere Meeresbewohner Stacey Starfish, Sherman Shark und Co. von früher sind wieder aufgetaucht und imitieren unsere User.

Die Team-Achievements werden nicht nur beispielhaft aufgebaut sondern haben auch Einzug in die App gehalten. Bei den Team-Details hatten wir bisher einen leeren Tab für die Achievements. Wir wollten schon alleine aus Konsistenzgründen dort die Achievements genauso anzeigen wie in der MyAchievements-Übersicht. Dazu haben wir das MyAchievement-Fragment abstrahiert und dann ebenfalls in der Team-Ansicht eingebunden. Somit hatten wir blitzschnell einen neuen Screen, worüber wir selbst etwas überrascht waren. An den Achievements wollen wir App-seitig in der kommenden Woche weiterarbeiten. Hier haben wir noch einige Ideen und haben uns gleich noch ein paar Backlog-Items angelegt, um sicher zu gehen, dass uns die Arbeit nicht ausgeht 😉

Da wir auf Templates für unsere Beispieldaten umgestellt haben, war es nicht schwierig, unsere bereits für die Achievements verwendeten Images nochmals für die Teams und User wiederzuverwenden. Nach dem Hinzufügen zu den Beispieldaten wurden diese prompt in der App angezeigt. In diesen Momenten müssen wir immer wieder schmunzeln, weil uns solche Dinge durch die monatelange, durchdachte Entwicklung und absolutem Frickelverbot geschenkt werden.

Außerdem haben wir uns nochmals mit unserem ServiceProvider befasst. Hier hatten wir aufgrund der Type Erasure zur Laufzeit an Flexibilität eingebüßt und mussten direkt auf die konkrete Klasse zugreifen. Das haben wir mittlerweile behoben, sind jedoch noch nicht hundertprozentig glücklich mit unserer Extension-Function auf dem IServiceProvider-Interface, da wir nun wieder zwei Methoden jeweils für get- und setService auf dem Interface haben. Vielleicht fällt uns hier noch eine bessere Lösung ein.

Und zu guter Letzt haben wir wieder ein bisschen etwas für die Dev-Usability getan. Bisher ist der Webserver gecrashed, wenn wir den Testmode aktiviert, bzw. im Umkehrschluss die Security deaktivert hatten, aber einen Endpunkt aufgerufen haben, der einen Principal erwartet, der dann nicht mehr mitgegeben wurde. Durch eine saubere Exception konnten wir sogar den Anblick unseres ErrorOverlays in der App genießen und uns wieder einmal über unsere ganze Vorarbeit in den letzten Wochen freuen.

2021-02-23 A bit of everything (Achievements, Bugfixing, Generics, Konzept-Doku)

Wenn wir so überlegen, haben wir in den letzten beiden Tagen nicht an einem konkreten Thema gearbeitet, sondern viele kleine Dinge erledigt.

Begonnen haben wir mit der Vervollständigung der User Achievements. Das Backend und das Erzeugen von Achievements beim Starten des Webservers, die später einem User oder Team zugeordnet werden können, hatten wir bereits im letzten Sprint implementiert; die Anzeige der Achievements in der App in diesem Sprint abgeschlossen.
Größeren Aufwand steckten wir in das Design des Hintergrunds unserer Fragments. Im Test haben wir viele, kleine Bilder im Hintergrund angezeigt, die einen Kontrast herstellen sollen, um die eigentlichen Elemente im Fragment darüber zu legen. Wenige, viele, größere, kleinere Bilder … am Ende hatte es nicht den gewünschten Effekt. Da werden wir also noch einmal ran müssen.

Wichtig war für uns der Umgang mit Generics. Da wir aus dem ABAP-Umfeld (in dem wir aktuell noch arbeiten) keine Generics kannten, gibt es für uns über Generics immer noch eine Menge zu lernen. Also haben wir uns in diesem Thema etwas weitergebildet. Gebraucht haben wir sie für unseren Service Provider. Bisher musste beim Beschaffen eines Services der Typ des Services an den Service Provider übergeben werden. Nachteilig daran ist die fehlende Typsicherheit. Da wir Services in einer Map cachen, und Generic-Types zur Designzeit nicht bekannt sind, ist der Zugriff auf die Laufzeitinformation der angeforderten Services gar nicht so einfach (Stichwort inline function und reified).
Außerdem haben wir den Unterschied zwischen <*> und <Any> verstanden. In jeder Hinsicht war es lehrreich.

Was ging sonst so?
Feintuning an den Achievements: chronologische Sortierung, Progress Inidicator für bereits erreichte Achievements ausblenden, die Bilder ohne einen Runden „Rahmen“ anzeigen
Textharmonisierung: Texte gruppiert, doppelte Texte gelöscht und die Anzeige der Texte auf dem Dashboard mit den angezeigten Texten im zugehörigen Fragment vereinheitlicht.
Prüfungen beim App-Start: Überprüfung, ob der IdProvider und der Webserver erreichbar sind.
Last but not least haben wir begonnen einen Teil unserer umgesetzten Framework Konzepte zu dokumentieren.

Zum guten Schluss begannen wir in den letzten Stunden mit der Umsetzung mehrerer Builder, die wir nutzen wollen, um im Backend Achievement Templates, User-Achievements, User und später auch Teams und zugehörige Achievements beim Webserver-Start zu erzeugen. Die Builder sollen einfach verwendbar sein. Wird keine Konfiguration des zu erzeugenden Objektes vorgenommen, entscheidet der Builder.
An der Fertigstellung werden wir sicherlich noch in den kommenden Wochen arbeiten.

2021-02-18 Setup Backend Achievements und Error Knowledge Database

Im letzten Sprint haben wir mit der Umsetzung des UIs für Achievements angefangen. In diesem Sprint haben wir uns an das Backend gemacht. Unser Ziel war es nicht nur dem Benutzer Achievements zuzuordnen, sondern wir wollen auch die Möglichkeit haben einem Team Achievements zuzuordnen. Letztendlich werden diese Achievements aber durch die Mühe von Benutzern erreicht. Deshalb haben wir uns überlegt Team-Achievements einzuführen, die über ein Team eingesehen werden können. Letztendlich wird aber jedem User – der an einem Team-Achievement mitgewirkt hat – auch seinem User Profil gutgeschrieben.
Die Umsetzung dafür war allerdings gar nicht so einfach, denn wir haben parallel auch User-Achievements und übergeordnet eine Achievement Klasse eingeführt. Außerdem gibt es Achievement Templates, die unserer Meinung nach ebenfalls in Team- und User-Achievement-Templates aufgeteilt werden müssen.
Ein Template ist eine Vorlage eines Achievements, dem Bild und Titel zugeordnet sind. Erreicht ein Team oder ein Benutzer ein Achievement-Ziel, wird das Template dem konkreten Team- oder User-Achievement zugeordnet. D.h. das konkrete Achievement hat einen Zeitstempel, wann das Achievement erreicht wurde und bezieht die Stamminformationen wie Bild und Titel aus dem Template.
Damit sollten wir fürs erste auch zukünftige Erweiterungen berücksichtigen können.

Das nächste größere Problem waren die Achievement-Templates. Solche Art Stammdaten hatten wir noch nicht. Bisher gibt es nur Bewegungsdaten wie ein neues Team, das ein Benutzer anlegen kann. Da wir aktuell noch mit einer In-Memory-Datenbank im Backend arbeiten, die bei jedem Neustart reseted wird, haben wir nach einer Lösung gesucht, die Achievement-Templates beim Starten des Webservers zur Verfügung zu stellen. Dank Spring Boot gar nicht so schwierig. Über die Annotation @PostConstruct kann eine Methode einer Komponente nach der allgemeinen Initialisierung des Servers instanziiert und ausgeführt werden. Über ein Verzeichnis können wir Bilder für Achievements bereitstellen. Wurde der Server gestartet, erzeugen wir Achievement-Templates, ordnen die Bilder zu, die wir im Verzeichnis bereitgestellt haben und speichern die Templates.

Nachdem wir dieses Problem gelöst hatten, wollten wir die Achievements aus dem Backend in der App zur Anzeige bringen. Nachdem wir einen Fehler im Backend gesucht haben, hatten wir die Idee unser Technical Error Overlay um Handlungsanweisungen zu erweitern. Denn Fehler wie: „Der aufgerufene Endpunkt ist nicht implementiert“ oder „Der Webserver ist nicht gestartet“ können häufiger auftreten. Warum nach dem Fehler suchen, wenn wir die Lösung des Problems schon kennen, nachdem wir danach gesucht haben.
Also haben wir mit dem Aufbau einer Wissensdatenbank begonnen. Tritt ein Fehler auf, der im Technical Error Overlay angezeigt wird, dann schauen wir nun in unserer Error Knowledge Database nach, ob es zu diesem Fehler bereits Einträge mit möglichen Lösungen gibt. Wenn ja, dann zeigen wir auch diese im Technical Error Overlay an.
Wir hoffen, dass uns das beim Beheben zukünftiger Probleme hilft. Wir finden es cool. Und außerdem ist es schön, dass wir uns um Themen kümmern können, die uns wichtig sind und nicht extern getrieben sind. Hier ein Beispiel:

Technical Error Overlay mit Anzeige möglicher Lösungen

Im nächsten Sprint werden wir mit der Anzeige der Achievements weiter machen.