Comment gérer plusieurs Service Desks


Publié par
Nicolas ESTEVES

4 octobre 2017

Travailler avec Jira Service Desk est avant tout un choix qui permet de minimiser le temps de traitement des tickets tout en améliorant la qualité des réponses apportées. De tels avantages attirent forcément, ce qui a une incidence directe sur le nombre d’équipes souhaitant l’utiliser, et donc indirectement, sur le nombre de projets Service Desk sur une même instance Jira. Si l’on ajoute à cela la polyvalence de certains agents impliqués dans plusieurs équipes, on constate avec le temps le paradoxe suivant: plus un agent travaille sur différents projets Service Desk, moins il sera efficient.

L’explication à ce phénomène est assez simple à trouver : actuellement, Jira Service Desk ne propose pas d’interface agrégeant dans une seule vue tous les tickets de chaque projet auquel a accès un agent. Ce dernier doit donc obligatoirement basculer d’un projet à l’autre pour voir les files de tickets qui sont rattachées à chacun d’entre eux. Plutôt contre-productif, n’est-ce pas? Dans certains cas extrêmes, cette problématique amène les agents à revenir à un tableau de bord Jira classique, se privant ainsi d’une des fonctionnalités phares de Jira Service Desk : son interface pour les agents.

Il existe pourtant des solutions !

Dans cet article, nous allons voir dans quel contexte Valiantys a dû faire face à ce problème, et surtout comment nous l’avons contourner. Pour les plus intéressés d’entre vous, nous aborderons également dans la dernière partie des détails un peu plus techniques sur la réalisation ainsi que quelques solutions alternatives.

L’architecture du support Valiantys

Vous le savez sans doute, Valiantys propose une offre de support sur les produits Atlassian. Tout comme de nombreux clients, nous avons fait le choix de Jira Service Desk pour optimiser le plus possible cette activité. Notre équipe de support utilise donc tous les jours Jira Service Desk au travers d’un projet central qui se nomme Valiantys Support (VS).

Avec l’évolution de l’offre de support, et les fonctionnalités désormais proposées pour les contrats « illimités », nous avons dû créer de nouveaux projets de support, chacun entièrement dédié à un client. Nous nous retrouvons donc dans le cas présenté au début de cet article: nos agents doivent travailler sur plusieurs projets de type Service Desk…

Prenons l’exemple d’un projet de support pour un client ayant un contrat « illimité » qui se nomme Client Support (CS), lui aussi de type Service Desk bien entendu. Nous avons donc des demandes qui sont créées dans les deux projets, mais que nous devons aussi prioriser entre elles afin de respecter tous les SLAs mis en place. Avec un seul projet CS, il était envisageable de continuer à travailler en basculant entre les deux projets de support. Mais avec une augmentation quasi constante du nombre de projets similaires au projet CS, nous nous rapprochions dangereusement du cas où l’équipe de support perdait en productivité. Il a donc fallu trouver une solution à cette problématique.

Le projet Valiantys Support de type Service Desk.

Synchroniser des demandes entre deux projets Jira Service Desk

L’idée est simple: afin que les agents n’aient pas à basculer entre tous les projets, il faut pouvoir faire apparaître dans les files du projet principal (VS) les demandes qui sont dans l’autre projet (CS) sur un même écran. La réalisation est quant à elle plus compliquée. En effet, Jira Service Desk ne permet pas d’inclure dans les files des demandes provenant d’autres projets:

Aucune demande CS affichée dans VS, alors qu’il y en a bien.

Partant de ce constat, la solution à base de synchronisation s’est imposée d’elle-même. A chaque demande créée dans un projet CS, on crée automatiquement une demande équivalente dans le projet VS. Et par la suite, nous synchronisons chaque mise à jour effectuée sur la demande CS dans la demande correspondante dans le projet VS afin de toujours avoir une vue cohérente. Voyons à quoi cela ressemble concrètement:

Création d’une demande CS.

Demande vue par le client via le portail du projet CS.

Maintenant que la demande est créée, voici ce que voient les agents dans le projet VS:

On notera la présence des deux références: CS et VS.

Quant à ce que pourraient voir les agents dans le projet CS, nul besoin d’en parler puisque le but ici est justement qu’ils n’aient pas à aller le voir.

Afin de ne pas perturber le client avec plusieurs références, tout le travail est fait dans la demande CS: le passage des transitions, les commentaires, les éditions, etc… La demande VS n’existe que pour donner une vue des éléments clés de la demande d’origine. Voici ce que voit un agent lorsqu’il clique sur chaque référence:

 

Demande CS: la demande sur laquelle l’agent doit travailler.

Demande VS: les transitions sont bloquées, et un message invite l’agent à aller vers la demande CS.

Grâce à la synchronisation, nous avons donc centralisé dans un seul et même projet Service Desk toutes les demandes liées à l’activité de support. La seule différence notable pour les agents lorsqu’ils travaillent sur des demandes synchronisées, c’est qu’il doivent le faire sur la demande CS, et non la demande VS.

Voyons désormais tout ce qui concerne la réalisation technique mise en place pour arriver à un tel résultat.

La réalisation technique

Deux apps sont utilisées pour gérer la synchronisation.

Exocet – automatiser la création de tickets liés

Exocet permet de créer automatiquement une demande VS lorsqu’une demande est créée dans CS. Durant cette opération, il lie également les deux demandes entre elles. Ce lien permettra entre autre de gagner en lisibilité, mais aussi de synchroniser les champs des demandes.

Le mapping des champs

Dans cette partie, on précise la correspondance des champs entre les futures demandes liées. Aucune praticularité dans la configuration à souligner. Pour la documentation, c’est ici.

Dans notres cas, les projets VS et CS sont très similaires, donc tous les champs sont mappés sur eux-même.

L’opération de création

C’est une opération avec une configuration des plus classiques. Si jamais vous souhaitez consulter la documentation associée, c’est ici que ça se passe.

On affiche pas l’opération car celle-ci est utilisée directement par post-fonction dans le(s) workflow(s) du projet CS.

 Ici, on spécifie dans quel cas l’opération est disponible.

 Il faut penser à utiliser le mapping créé à l’étape précédente.

La synchronisation

Enfin, on configure la synchronisation de la sorte. Pour plus d’informations, voir ici.

Pour cette partie, on veille à également à utiliser le mapping déjà créé.

Script Runner – synchroniser le passage des transitions grâce à des scripts Groovy

Nous utilisons Script Runner afin de gérer la synchronisation du passage des transitions, afficher des champs dynamiques dans les demandes, et conditionner les transitions.

Le Script Listener

Voici le Script Listener qui est utilisé:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.user.ApplicationUsers
import com.atlassian.jira.bc.issue.IssueService.TransitionValidationResult
import com.atlassian.jira.bc.issue.IssueService
import com.atlassian.jira.issue.IssueInputParameters
import javax.servlet.http.HttpServletRequest
import com.atlassian.jira.web.ExecutingHttpRequest
import com.atlassian.jira.event.type.EventDispatchOption
import org.apache.log4j.Category
  
// Debug
//Category log = log
//log.setLevel(org.apache.log4j.Level.DEBUG)
  
// Managers
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def eventManager = ComponentAccessor.getIssueEventManager()
def userManager = ComponentAccessor.getUserManager()
def issueService = ComponentAccessor.getComponent(com.atlassian.jira.bc.issue.IssueService)
  
// Constantes et variables
def issue = event.getIssue()
def myLink = 10640 // Synchronization link
def user = event.getUser()
def Issue linkedIssue = null
def transitionId = null
 
// Recherche d'une demande liée à synchroniser
def inwardLinks = issueLinkManager.getInwardLinks(issue.getId()) // Dans un sens
for (IssueLink oneLink : inwardLinks) {
    if (oneLink.getLinkTypeId() == myLink){
        linkedIssue = oneLink.getSourceObject()
    }
}
if (linkedIssue == null){
    def outwardLinks = issueLinkManager.getOutwardLinks(issue.getId()) // Puis dans l'autre
    for (IssueLink oneLink : outwardLinks) {
        if (oneLink.getLinkTypeId() == myLink){
            linkedIssue = oneLink.getDestinationObject()
        }
    }
}
  
// Identification de la transition passée
HttpServletRequest request = ExecutingHttpRequest.get()
if (request != null){
    transitionId = request.getParameter("action")  
    // Exécution de la transition sur la demande liée
    def transitionNum = Integer.parseInt(transitionId)
    if (transitionId != null && transitionId && user.getName() != "robot" && linkedIssue != null){
        // On éxécute avec le robot pour éviter les boucles
        def userRobot = userManager.getUserByName("robot")
        def transitionResult = issueService.validateTransition(userRobot, linkedIssue.getId(), transitionNum, issueService.newIssueInputParameters())
        if (transitionResult.isValid()){
            issueService.transition(userRobot, transitionResult)
        }
    }
}

Ce Script Listener écoute les transitions qui sont passées sur les demandes CS, et les réplique sur les demandes VS correspondantes. Nous utilisons un utilisateur « robot » pour passer ces transitions.

Ce script fonctionne uniquement dans le cas où les demandes synchronisées utilisent le même workflow. En effet, le Script Listener capte le numéro de la transition passée sur la CS, et utilise le même pour passer la transition sur la VS. Si les workflows sont différents, il faut manuellement faire un mapping entre les IDs des transitions de chaque workflow (ou alors choisir une alternative – voir plus bas).

Les Scripted Fields

Deux Scripted Fields sont utilisés dans le cas d’utilisation présenté dans cet article:

  • Le champ Sync Key, qui permet d’afficher la référence CS dans le ticket VS, notamment en tant que colonne dans les files:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.config.properties.APKeys
import org.apache.log4j.Category
 
//Category log = log
//log.setLevel(org.apache.log4j.Level.DEBUG)
 
def Long LINK_ID = 10640
def Long VS_ID = 13141
 
def baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL)
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def Issue linkedIssue = null
 
def issueLinksIn = issueLinkManager.getInwardLinks(issue.getId())
for (IssueLink oneLink : issueLinksIn) {
    if (oneLink.getLinkTypeId() == LINK_ID && oneLink.getSourceObject().getProjectId() != VS_ID){
        linkedIssue = oneLink.getSourceObject()
        break
            }
}
 
if (linkedIssue){
   '<a href="' + baseUrl + '/browse/' + linkedIssue.getKey() +'">' + linkedIssue.getKey() + '</a>'
}

Son fonctionnement est assez simple; il recherche dans les demandes liées à la demande VS celle qui utilise le lien mentionné dans la configuration Exocet. Ce qu’il retourne n’est autre qu’un lien (cliquable) vers cette demande.

  • Le champ Synchronized Issue qui sert à afficher un message d’avertissement:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.link.IssueLink
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.config.properties.APKeys
import org.apache.log4j.Category
 
//Category log = log
//log.setLevel(org.apache.log4j.Level.DEBUG)
 
def Long LINK_ID = 10640
def Long VS_ID = 13141
 
def baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL)
def issueManager = ComponentAccessor.getIssueManager()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()
def Issue linkedIssue = null
 
def issueLinksIn = issueLinkManager.getInwardLinks(issue.getId())
for (IssueLink oneLink : issueLinksIn) {
    if (oneLink.getLinkTypeId() == LINK_ID && oneLink.getSourceObject().getProjectId() != VS_ID){
        linkedIssue = oneLink.getSourceObject()
        break
            }
}
 
if (linkedIssue){
    def sync = '<a href="' + baseUrl + '/browse/' + linkedIssue.getKey() +'">' + linkedIssue.getKey() + '</a>'
    def writer = "<div class=\"aui-message aui-message-error\">" +
        "<p class=\"title\">" +
        "<strong>Please work on the synchronized issue: " + sync + "</strong>" +  
        "</p>" +
        "<p>The current one has been created for tracking purposes only.</p>" +
        "</div><!-- .aui-message --> "
    return writer
}

Même principe que le champ précédent, mais cette fois on y ajoute les formes et on affiche ce champ dans la demande.

Les conditions scriptées

Enfin, on conditionne les transitions de notre workflow afin de ne pas les afficher sur la demande VS si celle-ci est liée à une demande CS. Pour cela, nous utilisons une Simple Scripted Conditions (sur chaque transition).

cfValues['Synchronized Issue'] == null || currentUser.username == "robot"

Les transitions s’affichent uniquement si le retour est « vrai ». (coche)

On notera que l’utilisateur robot voit les transitions dans tous les cas. C’est un prérequis pour qu’il puisse passer les transitions dans le Script Listener.

Les alternatives

Réaliser la synchronisation des transitions avec le module Automation de Jira Service Desk

Il est possible de réaliser la synchronisation des transitions sans Script Listener. On peut par exemple utiliser le module Automation de Jira Service Desk. Celui-ci permet de passer une transition sur une demande du projet Service Desk lorsqu’une demande liée à également été transitée.

 

Avantage
Inconvénient
  • Cette solution est assez simple à mettre en place.
  • Cette solution n’est pas générique.
  • Pas besoin d’écrire une ligne de code.
  • A chaque modification du workflow (ajout ou retrait de transitions), il faudra modifier la règle Automation pour prendre en compte ces modifications.  Le Script Listener lui applique dans tous les cas la même transition quoi qu’il arrive.

Queues : une app dédiée à la création de files multi-projets

Valiantys a fait le choix de la synchronisation car c’est la solution la plus fiable et la plus flexible pour les besoins notre support. Néanmoins, il existe depuis peu d’autres solutions qui peuvent peut-être convenir dans d’autres cas.

Récemment lancée sur la Marketplace Atlassian, Queues permet d’intégrer des demandes provenant d’autres projets dans les files d’un projet Service Desk. Si cette app répond à la problématique présentée dans cette article, elle a tout de même quelques limitations : elle ne permet par exemple pas d’afficher tous les types de champs que l’on souhaite dans les colonnes. Queues remplace en réalité l’affichage classique des files de Jira Service Desk.

Le mot de la fin

Jira Service Desk est adopté par de plus en plus d’équipes. Il y a donc de fortes chances que vous soyez confronté à cette problématique un jour. Désormais, vous avez les cartes en main pour proposer à vos équipes la solution la plus adaptée à leurs besoins.

Et si jamais vous avez besoin d’un coup de pouce pour relever ce défi, n’hésitez pas à faire appel à un de nos consultants qui se fera un plaisir de vous aider à implémenter cette solution !