JIRA REST Client mit AngularJS

AngularJS ganz einfach

Rakia Ben Sassi
© Shutterstock/Daimond Shutter

Über REST-Services läuft die Kommunikation mit modernen Anwendungen und Cloud-Diensten wie Twitter oder GitHub. JIRA, das Projektmanagementtool von Atlassian, bietet auch ein REST-API an. In diesem Artikel stellen wir, durch die Entwicklung eines JIRA REST Clients, das JIRA REST API und das AngularJS-Framework vor. Wir erläutern grundlegende Konzepte von AngularJS wie die Umsetzung des Model View Controller Patterns und die Einsetzung von Scopes und Services. Im Internet findet man viele AngularJS-Tutorials und Beispiele, in denen Tools wie Node.js, npm, Bower und Grunt erwähnt und benutzt werden. Für unsere Applikation und zum Zwecke der Vereinfachung wird keines dieser Tools gebraucht. AngularJS, ein Browser und ein JIRA-Konto sind völlig ausreichend.

Das JIRA Rest API wurde mit dem Ziel entworfen, Entwicklern und Administratoren folgende Möglichkeiten zu bieten:

• Atlassian- oder andere Anwendungen mit JIRA zu integrieren
• Prozesse/Features von JIRA zu automatisieren
• Gadgets für JIRA zu entwickeln

Das API erlaubt den Zugriff auf eine Reihe von Ressourcen via URI.

http://hostname/rest/<api-name>/<api-version>/<resource-name>

<api-name> ist der Name des JIRA REST API. Sein Wert ist api.
<api-version> bezeichnet die Version des REST-API. Um die letzte Version des JIRA REST API zu nutzen, kann man einfach latest verwenden. 2 ist die aktuelle Version.
<resource-name> JIRA-REST-API-Ressourcen sind issue, user, attachment usw.

Setzen wir all dies zusammen, würde der URI eines Issues mit dem Key RES-6706 so aussehen:

http://JIRAserver/rest/api/2/issue/RES-6706

Via AngularJS auf die Daten von JIRA zugreifen

Das Framework AngularJS zeichnet sich dadurch aus, dass es viele Aspekte moderner JavaScript-Anwendungen unterstützt. Es fördert die Modularisierung von JavaScript-Logik sowie die bidirektionale Datenbindung zwischen View (HTML) und Model anhand der Implementierung des Patterns MVVM (Model View ViewModel). Die im Folgenden beschriebene Anwendung dient zur Einführung in AngularJS und das JIRA REST API (Abb. 1).Abb. 1: Architekturdiagramm

Sie besteht aus vier Bereichen, die es dem User ermöglichen, Stories zu suchen, anzuzeigen und zu erstellen:

1. Mit dem Log-in-Formular (Abb. 2) kann der Anwender sich an JIRA anmelden.
2. Mit dem Suchformular (Abb. 3) kann der eingeloggte User nach Stories suchen, indem er entweder dessen Projekt, Issue-Nummer (Key) und Beschreibung erfasst.
3. Die gewünschten Stories werden anschließend unten angezeigt. Sie können nach einem vom User einzugebenden Text gefiltert werden.
4. Im Bereich „Erstellen“ besteht die Möglichkeit, eine Story unter dem eingegebenen Projekt zu erstellen (Abb. 4).

Abb. 2: Log-in-Formular

Abb. 3: Suchformular

Abb. 4: Neue Issue erstellen

Abb. 5: Projektstruktur

Der gesamte Quellcode ist auf zwei Dateien verteilt:

index.html
angular-module.js

Zusätzlich werden unter index.html noch zwei externe Dateien aufgerufen (Listing 1).

• Twitter Bootstrap: bootstrap-combined.min.css
• Die minified-Version von AngularJS: angular.min.js

<html>
  <head>
    <meta charset="utf-8">
      <title>JIRA REST Client with AngularJS</title>
      <link rel="stylesheet" href="style.css"/>
      <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-com..." rel="stylesheet">
        <script src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
        <script type="text/javascript" src="angular-module.js"></script>
  </head>
  <body>
    <div ng-app="JIRARESTClientApp">
      <div ng-controller="ApplicationController">
        <div class="well">
          <form name="loginForm" ng-submit="login(credentials)">
            <div>
              <label for="JIRAServer">JIRA Server:</label>
              <input type="text" id="JIRAServer" ng-model="JIRAServer">
            </div>
            <div>
              <label for="username">Username:</label>
              <input type="text" id="username" ng-model="credentials.username">
            </div>
            <div>
              <label for="password">Password:</label>
              <input type="password" id="password" ng-model="credentials.password">
            </div>
            <div>
              <button type="submit">Login</button>
            </div>
          </form>
        ...

Der Container <div ng-app=“JIRARESTClientApp“> ist, zum Zweck des auto-bootstrap unserer Anwendung, mit dem Attribut ng-App gesetzt. ngApp ist eine Directive, die das Root-Element einer Applikation bezeichnet. AngularJS strukturiert Anwendungen anhand von Modulen. Ähnlich wie Packages unter Java kapselt ein Modul wiederverwendbare Programmteile und Konfigurationsinformationen. Listing 2 zeigt die Bereitstellung eines Moduls JIRARESTClientApp mit einem Service und einem Controller.

var module = angular.module('JIRARESTClientApp', []);
module.factory('Base64', function() {
    ...
});
module.controller('ApplicationController', ['$scope', '$http', 'Base64', function ($scope, $http, Base64) {
      $scope.JIRAServer = 'https://hostname';
      ...
    }
]);

Da wir kein Modul zu importieren haben, übergibt unser Modul lediglich ein leeres Array. Sonst hätten wir Folgendes geschrieben, um beispielsweise das ngResource-Modul zu importieren:

var module = angular.module('JIRARESTClientApp', [ngResource]);

Die Methode controller des Objekts module ist hier angewandt worden, um einen neuen Controller, ApplicationController, einzurichten. Der erste Parameter ist der Name des Moduls. Beim zweiten handelt es sich um eine Funktion, die die Logik kapselt. Damit AngularJS die Abhängigkeiten injizieren kann, müssen die Namen der Parameter denen der zu injizierenden Abhängigkeiten (Services in AngularJS) entsprechen. Im Controller kommen die Parameter $scope, $http und Base64 zum Einsatz. Der Parameter $scope zeigt auf ein Objekt, das Variablen, die in einem bestimmten Teil der Anwendung gültig sind, enthält. Die Aufgabe eines Controllers liegt im Bereitstellen von Informationen über dieses Objekt. Dabei handelt es sich um das Modell im Sinne von MVC. Im Objekt $scope haben wir neue Eigenschaften (JIRAServer, …) eingerichtet. Diese verweist auf das zu verwendende View Model.
Der Parameter $http bezeichnet einen Dienst, der im Lieferumfang von AngularJS enthalten ist und auf einfache Weise den Zugriff auf Ressourcen via HTTP erlaubt. Im Kontext von AngularJS werden Services als wiederverwendbare Objekte bezeichnet, die über Dependency Injection zu beziehen sind. Base64 ist kein von Angular vorgegebener Service, sondern in der JIRARESTClientApp anhand der factory-Methode erstellt worden und wird für die HTTP-Basic-Authentifizierung verwendet.

Aufmacherbild: Silhouette communication antenna tower von Shutterstock / Urheberrecht: Daimond Shutter

[ header = Seite 2: JIRA-Authentifizierung ]

JIRA-Authentifizierung

JIRA ermöglicht REST-Clients, sich mittels Basic Authentication mit einem Benutzernamen und einem Passwort zu authentifizieren (Listing 3).

$scope.credentials = { username: 'yourUserName', password: 'yourPassword'};
$scope.JIRAServer = 'https://hostname';
$scope.login = function (credentials) {
  $http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"};
  $http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode($scope.credentials.username + ':' + $scope.credentials.password);
  $http({method: 'GET', url: $scope.JIRAServer})
  .success(function(data) {
      console.log("Auth success: " + JSON.stringify(data));
  });
};

Auf die Issues eines bestimmtes Projekts zugreifen

Um alle Issues, die zum Projekt RES gehören, zu erhalten (Abb. 6), ist der folgende URI aufzurufen:

http://JIRAserver/rest/api/2/search?jql=project=RES

Mit der Ergänzung des JQL-Befehls mit ORDER BY createdDate wird das Ergebnis nach Erstelldatum sortiert:

http://JIRAserver/rest/api/2/search?jql=project=RES ORDER by createdDate

JQL steht für JIRA Query Language, die mit der Datenbankskriptsprache SQL vergleichbar ist. Die JQL-Abfrage kann man in JIRA nach einem Klick auf ERWEITERT im Issues-Filter sehen (Abb. 7).

Abb. 6: JIRA-Issues-Filter: Standardmodus

Abb. 7: JIRA-Issues-Filter: Erweiterter Modus (JQL-Befehl)

Mit der Methode getIssues ergänzen wir den Controller, um die Daten vom REST-API auszulesen (Listing 4).

$scope.getIssues = function () {
  var request = $http({
      method: "GET",
        url: JIRAServer + "/rest/api/2/search?jql=project=" + $scope.project +"%20AND ORDER BY createdDate"
  });
  request.success( function(data) {
      $scope.JIRAIssues = data.issues;
      console.log("JIRA response: " + JSON.stringify(data));
  });
};

Nach erfolgreichem HTTP-Abruf liefert JIRA als Response ein JSON-Objekt. Dieses Objekt enthält ein Element issues, das lediglich ein Array von Issues ist. Es wird in der Variable JIRAIssues gespeichert (Listing 5).

{ "expand" : "schema,names",
  "issues" : [ { "expand" : "editmeta,renderedFields,transitions,changelog,operations",
      "id": "32659",
      "key": "RES-1917",
      "fields" : {
        "assignee" : null,
        "updated": "2014-06-26T13:20:18.000+0000",
        "created" : "2014-06-26T13:13:41.000+0000",
        "description": "User can inherit permissions.",
        "fixVersions" : [  ],
        "summary": "Cross origin",
        "issuetype" : { "id" : "16",
          "name" : "Story",
          "subtask" : false
        },
        "priority" : { "id" : "3",
          "name" : "Major",
          "self" : "http://JIRAServer/rest/api/2/priority/3"
        },
        "progress" : { "progress" : 0,
          "total" : 0
        },
...

Nun wird eine einfache Liste von Daten angezeigt (Abb. 8).

Abb. 8: JIRA Issues

Der Issues-Filter wird auf die vorhandenen Daten angewandt und aktualisiert die Ansicht (Listing 6).

<form>
  <div>
    <input type="text" ng-model="filterText" placeholder="Issues Filter">
    <button ng-click="filterText = null" class="btn">Filter</button>
  </div>
</form>
<table class="table table-striped table-bordered">
  <thead>
    <tr>
      <th class="span1">Key</th>
      <th class="span2">Title</th>
      <th class="span1">State</th>
      <th class="span2">Created</th>
      <th class="span2">Updated</th>
      <th class="span1">Description</th>
      <th class="span2">Reporter</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="issue in JIRAIssues | filter:filterText">
      <td>{{issue.key}}</td>
      <td>{{issue.fields.summary}}</td>
      <td>{{issue.fields.status.name}}</td>
      <td>{{issue.fields.created | date:'short'}}</td>
      <td>{{issue.fields.updated | date:'short'}}</td>
      <td>{{issue.fields.description}}</td>
      <td>{{issue.fields.reporter.displayName}}</td>
      <td>{{issue.fields.assignee.displayName}}</td>
    </tr>
  </tbody>
</table>

Um eine neue Issue in JIRA zu erstellen, haben wir die Methode createIssue im Controller hinterlegt (Listing 7). Die vollständige Anwendung kann man auf GitHub finden.

$scope.createIssue = function () {
  var request = $http({
      method: "POST",
      url: $scope.JIRAServer + "/rest/api/2/issue",
      data: {
        "fields": {
          "project": { 
            "key": "RES"
          },
          "summary": "New Issue from JIRARESTCientApplication",
          "description": $scope.description,
          "issuetype": {
            "name": "Bug"
          }
        }
      }
  });
  request.success( function(data) {
      console.log("JIRA response: " + JSON.stringify(data));
  });
};

Fazit

In diesem Artikel haben wir Daten aus dem JIRA REST API mit AngularJS und seinem $http-Service ausgelesen und dargestellt. In einem zukünftigen Artikel zeigen wir, wie man den $resource-Service, der die Interaktion mit REST-Server-side-Daten ermöglicht, benutzen kann.

Geschrieben von
Rakia Ben Sassi
Rakia Ben Sassi
Rakia Ben Sassi ist Software Engineer und arbeitet seit 2009 als Freiberuflerin. Sie verfügt über langjährige Erfahrung im Bereich Webentwicklung mit Java, JavaScript und PHP und hat darüber hinaus Erfahrungen mit vielen Frameworks (z. B. Spring, Struts, Zend, AngularJS, jQuery, Extjs) gesammelt. Ihre Firma SAM Business Informatics unterstützt kleine, mittlere und große Unternehmen durch Beratung und Entwicklung.Web: www.sam-bi.com
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.