Entwicklung eines JIRA Gadgets Ein generisches Balkendiagramm 15.09.2011, Atlassian User Group Leo von Klenze, Alexander Arth 1 Motivation JIRA Überblick JIRA für das Beschwerdemanagement Auswertungen mit JIRA 2 Entwicklung von Gadgets 3 Komponenten eines JIRA Gadgets Definition des Plugins: atlassian-plugin.xml Definition des Gadgets: gadget.xml Nutzen der REST API 4 Testen des Plugins und Validierung Automatisierte Tests Validierung von Benutzereingaben 5 Das fertige Balkendiagramm Plugin 6 Zum Schluss Motivation Motivation JIRA Überblick Motivation JIRA Überblick � Bugtracker von Atlassian (http://www.atlassian.com/JIRA) � � Einfach zu bedienen (für den Benutzer) � Umfangreich zu konfigurieren (seit 4.4 etwas übersichtlicher �) Motivation JIRA für das Beschwerdemanagement � Eigener Workflow � Eigene Felder (Eigenschaften eines Tickets) � Verwendung im Fachbereich � Auswertungen notwendig In diesem Fall über Eingangsmonat und Herkunft Motivation Auswertungen mit JIRA � Auswertungen basieren auf Filtern Auswahl von Tickets mit bestimmten Eigenschaften � Verschiedene visuelle Darstellungen von Filtern möglich Motivation Auswertungen mit JIRA � Auswertungen basieren auf Filtern Auswahl von Tickets mit bestimmten Eigenschaften � Verschiedene visuelle Darstellungen von Filtern möglich Motivation Auswertungen mit JIRA � Auswertungen basieren auf Filtern Auswahl von Tickets mit bestimmten Eigenschaften � Verschiedene visuelle Darstellungen von Filtern möglich Motivation Ziel der Auswertung Motivation Ziel der Auswertung Motivation Berichte vs. Gadgets � Es gibt in JIRA sogenannte Berichte zur Auswertung � Allerdings nur wenige vordefinierte Berichte � Konfiguration muss jedesmal neu ausgewählt werden (Filter/Typ) � Gute Druckansicht Motivation Berichte vs. Gadgets � Gadgets sind sofort auf der Startseite sichtbar � Pro Startseite sind mehrere Gadgets möglich � Gadgets merken sich ihre Konfiguration � Zur Zeit keine gute “out-of-the-box”-Druckansicht Motivation Auswertungen mit Gadgets � Knapp 30 vordefinierte Gadgets � Nur wenige Gadgets, die Auswertungen auf eigenen Feldern ermöglichen � Problem: oft an spezielle JIRA-Felder gebunden, z.B. “Erstellt vs. Erledigt” � In diesem Fall benötigt: gestapeltes Balkendiagramm (Monat, Herkunft) Lösung: eigenes Gadget entwickeln � Entwicklung von Gadgets Entwicklung von Gadgets Allgemeine Vorteile von Gadgets � Sofortige Präsenz auf Startseite � Anpassbarkeit � Interaktivität � Cross Plattform Einsetzbarkeit (⇒ Open Social Api) Entwicklung von Gadgets Tools, Techniken und Sprachen � Build Tools (Maven) � Konfiguration: XML � Frontend: HTML, CSS � Backend: JavaScript, AJAX, Java Komponenten eines JIRA Gadgets Komponenten eines JIRA Gadgets atlassian-plugin.xml � Plugin Beschreibung � Definition von Plugin Komponenten Komponenten eines JIRA Gadgets atlassian-plugin.xml � Plugin Beschreibung � Definition von Plugin Komponenten <g a d g e t key=” b a r c h a r t ” l o c a t i o n= ”com/ t n g t e c h / j i r a / p l u g i n s / g a d g e t / b a r c h a r t −g a d g e t . xml ” /> < r e s t key=” r e s t −r e s o u r c e s ” p at h=” / j i r a −r e s t ” v e r s i o n=” 1 . 0 ”> < d e s c r i p t i o n> S t e l l t d i e nö t i g e n REST R e s o u r c e n f ü r d a s Gadget b e r e i t</ d e s c r i p t i o n> </ r e s t> Komponenten eines JIRA Gadgets Aufbau der gadget.xml � Allgemeine Beschreibung des Gadgets � Benötigte Module � User Authentifizierung � Einstellungsvariablen � HTML und JavaScript Code für zwei Ansichten – Konfiguration → Descriptor – Ergebnisansicht → View Komponenten eines JIRA Gadgets Aufbau der gadget.xml � Allgemeine Beschreibung des Gadgets � Benötigte Module � User Authentifizierung � Einstellungsvariablen � HTML und JavaScript Code für zwei Ansichten – Konfiguration → Descriptor – Ergebnisansicht → View ⇒ Funktionierendes Beispiel aus Vorlage ist sinnvoll. Tutorials von Atlassian funktionieren nicht mehr richtig mit aktuellen Versionen! Komponenten eines JIRA Gadgets Aufbau der gadget.xml [1] <Module> <M o d u l e P r e f s t i t l e =” B a l k e n d i a g r a m m ” a u t h o r=”TNG” d e s c r i p t i o n=” E i n g e n e r i s c h e s B a l k e n d i a g r a m m . ”> <R e q u i r e f e a t u r e=” o a u t h p o p u p ” /> .... #o a u t h </ M o d u l e P r e f s> <U s e r P r e f name=” i s C o n f i g u r e d ” d a t a t y p e=” h i d d e n ” d e f a u l t v a l u e=” f a l s e ” /> .... <Content t y p e=” h t m l ”><! [CDATA[ #r e q u i r e R e s o u r c e ( ” com . a t l a s s i a n . g a d g e t s . p u b l i s h e r : a j s − gadgets ”) .... #i n c l u d e R e s o u r c e s ( ) Komponenten eines JIRA Gadgets Aufbau der gadget.xml [2] < s c r i p t t y p e=” t e x t / j a v a s c r i p t ”> ( function () { v a r g a d g e t = AJS . Gadget ( { b a s e U r l : ” ATLASSIAN BASE URL ” , u se Oa ut h : ”/ r e s t / g a d g e t / 1 . 0 / c u r r e n t U s e r ” , config : { descriptor : function ( args ) { . . . . } } , view : { template : function ( args ) { . . . . } } }) ; }) ( ) ; </ s c r i p t > ]] > </Content> </Module> Komponenten eines JIRA Gadgets Gadgetkonfiguration: descriptor [1] descriptor : function ( args ) { v a r p r o j e c t O r F i l t e r P i c k e r = AJS . t h i s . f i e l d s . projectOrFilterPicker ( this , ” p r oj ect O r F ilt e r Id ” , args . options ) ; return { f i e l d s : [ projectOrFilterPicker , { userpref : ” axisField ”, l a b e l : ” x−Achse : ” , d e s c r i p t i o n : ” F e l d f ü r Werte d e r x−Achse . ” , type : ” s e l e c t ” , s e l e c t e d : t h i s . g e t P r e f (” a x i s F i e l d ”) , options : args . a x i s F i e l d . fieldNames }, AJS . t h i s . f i e l d s . n o w C o n f i g u r e d ( ) ] }; }, .... Komponenten eines JIRA Gadgets Gadgetkonfiguration: Möglichkeiten � Vorgefertigte Konfigurationsfelder, z.B. projectOrFilterPicker � Selbstgebaute Optionen aus Standardtypen, z.B. type=“select“ Komponenten eines JIRA Gadgets Gadgetkonfiguration: Möglichkeiten � Vorgefertigte Konfigurationsfelder, z.B. projectOrFilterPicker � Selbstgebaute Optionen aus Standardtypen, z.B. type=“select“ Vor- und Nachteile � Es gibt keine Seite mit allen Möglichkeiten � Einheitliche Namensgebung ermöglicht erfolgreiches Raten, z.B. filterPicker oder type=“text“ Komponenten eines JIRA Gadgets Gadgetkonfiguration: Möglichkeiten � Vorgefertigte Konfigurationsfelder, z.B. projectOrFilterPicker � Selbstgebaute Optionen aus Standardtypen, z.B. type=“select“ Vor- und Nachteile � Es gibt keine Seite mit allen Möglichkeiten � Einheitliche Namensgebung ermöglicht erfolgreiches Raten, z.B. filterPicker oder type=“text“ � Erzeugter HTML Code nicht perfekt → Checkbox geht im IE nicht! Komponenten eines JIRA Gadgets Gadgetkonfiguration: descriptor [2] descriptor : function ( args ) { .... , args : [{ key : ” a x i s F i e l d ” , ajaxOptions : function () { return { u r l : ”/ r e s t / j i r a −r e s t / 1 . 0 / B a r C h a r t C o n f i g u r a t i o n / LookupCustomFields ” }; } }] } Komponenten eines JIRA Gadgets Gadgetkonfiguration: descriptor [2] descriptor : function ( args ) { .... , args : [{ key : ” a x i s F i e l d ” , ajaxOptions : function () { return { u r l : ”/ r e s t / j i r a −r e s t / 1 . 0 / B a r C h a r t C o n f i g u r a t i o n / LookupCustomFields ” }; } }] } View wird analog definiert und modifiziert DOM direkt Komponenten eines JIRA Gadgets Java-REST-Klasse @Path ( ” / B a r C h a r t C o n f i g u r a t i o n ” ) @ P r o d u c e s ( { MediaType . APPLICATION JSON } ) public class BarChartGadgetConfiguration { public BarChartGadgetConfiguration ( J i r a A u t h e n t i c a t i o n C o n t e x t jAC , . . . . ) { . . . . } @GET @Path ( ” / L o o k u p C u s t o m F i e l d s ” ) p u b l i c Response lookupCustomFieldNames ( ) { try { F i e l d L i s t f i e l d L i s t = getFieldList () ; r e t u r n R e s p o n s e . ok ( f i e l d L i s t ) . b u i l d ( ) ; } catch ( RuntimeException e ) { r e t u r n Response . s e r v e r E r r o r ( ) . b u i l d ( ) ; } } .... } Komponenten eines JIRA Gadgets FieldList-Klasse � Objekt in Response beliebig Komponenten eines JIRA Gadgets FieldList-Klasse � Objekt in Response beliebig � Muss aber in Klassendefinition annotiert sein! � ⇒ Einfache Response Objekte wie Response.ok(”Hello World”) funktionieren nicht! Komponenten eines JIRA Gadgets FieldList-Klasse � Objekt in Response beliebig � Muss aber in Klassendefinition annotiert sein! � ⇒ Einfache Response Objekte wie Response.ok(”Hello World”) funktionieren nicht! @XmlRootElement public class FieldList { @XmlElement p u b l i c L i s t <Map<S t r i n g , S t r i n g >> f i e l d N a m e s ; p u b l i c F i e l d L i s t (Map<Long , S t r i n g > f i e l d I n f o s ) { // f i e l d N a m e s b e f ü l l e n } } Komponenten eines JIRA Gadgets Stolpersteine im Java Code � Exceptions im Java Code ⇒ Try Catch um Rest Aufruf und Response.serverError() Komponenten eines JIRA Gadgets Stolpersteine im Java Code � Exceptions im Java Code ⇒ Try Catch um Rest Aufruf und Response.serverError() � Rest Klassen werden als Singleton instanziiert ⇒ Threadsafe programmieren! (ThreadLocal) Komponenten eines JIRA Gadgets Stolpersteine im Java Code � Exceptions im Java Code ⇒ Try Catch um Rest Aufruf und Response.serverError() � Rest Klassen werden als Singleton instanziiert ⇒ Threadsafe programmieren! (ThreadLocal) � Standardeinstellung bzgl. Caching der REST Aufrufe varriiert ⇒ new CacheControl().setNoCache(true) an Response anhängen Testen des Plugins und Validierung Testen des Plugins und Validierung Unit- und Integrationstests � Testing Framework: JUnit � Mocking Framework: Mockito � Momentan 88 Unit Tests und 1 Integrations Test (Stand 15.9.) � Automatisiertes Laden der Testdaten für manuelles Testen der GUI Testen des Plugins und Validierung Wieso nur 1 Integrationstest? � Code Design: Code Coverage durch Unit Tests ca 71% � JIRA API bietet FuncTestCase Klasse als GUI Tester � Problem: Gadgets liegen in iFrames ⇒ DOM versteckt Testen des Plugins und Validierung Wieso nur 1 Integrationstest? � Code Design: Code Coverage durch Unit Tests ca 71% � JIRA API bietet FuncTestCase Klasse als GUI Tester � Problem: Gadgets liegen in iFrames ⇒ DOM versteckt � Mocken der kompletten JIRA-API komplex, aber möglich ⇒ Erstellen einer Klasse JiraUtils, die durchgereicht wird und als einzige Schnittstelle zur Jira API dient Testen des Plugins und Validierung Validierung von Benutzereingaben Javascript: � Validierung von Texteingaben Testen des Plugins und Validierung Validierung von Benutzereingaben Javascript: � Validierung von Texteingaben Java: � Setzen einer Flag, wenn die Konfiguration geändert wird � Weitergeben von Flag und User Input an Java-Klasse � Validierung z.B. ob gewähltes Custom Field korrekt unterstützt wird � Behandlung der Ergebnisse über window.alert() Das fertige Balkendiagramm Plugin Das fertige Balkendiagramm Plugin Konfiguration Das fertige Balkendiagramm Plugin Das Balkendiagramm Zum Schluss Hinweise � Atlassian JIRA: http://www.atlassian.com/software/jira/ � Dashboard einrichten: http://confluence.atlassian.com/display/ JIRA/Customising+the+Dashboard � JIRA Developer Documentation (Atlassian SDK, Plugin Guide, . . . ) http://confluence.atlassian.com/display/JIRADEV/JIRA+ Developer+Documentation � Das Plugin wird gerade im Atlassian-Plugin-Exchange veröffentlicht Vielen Dank für die Aufmerksamkeit!