Questions for Confluence license has expired.

Please purchase a new license to continue using Questions for Confluence.

Service Desk Customer issues and comments do not sync - no permission to create

 
1
0
-1

Environment:

2 Jira Servers

  • Jira Service Desk Source Project (CD)
  • Jira Service Desk Destination Project (GCD)


Configuration:

Both Projects are identical in terms of permissions setup. Both projects use Service Desk Customers Role for Customers (users without Jira License).


Customer users are same on both Jira. Password is different. (not user if password needs to be same)


Incoming sync rule:

issue.comments = commentHelper.mergeComments(issue, replica){ it.executor = nodeHelper.getUserByEmail(it.author?.email) }


Problem:

Issues opened by customer in CD are unable to sync to GCD.


There are two sub-issues:

  1. Exalate canont create ticket with the following error:


Error 1:

Exalate has problems while trying to create an issue in this Jira. Details: [InvalidInputException: [Error map: [{}]] [Error list: [[User 'customer@customer.com' doesn't have the 'Create Issues' permission]]]


   2. If we add the customer to Create Issues permission scheme, the issue is created, but get comment error.


Error 2:

It was not possible to create service desk comment from createParams `BasicCommentParameters{executor=customer@customer.com (JIRAUSER39169), commentId=null, created=2020-08-04 09:06:12.653, body=' [^TemenosBNKReleaseDocSSOWCF3rdAug2020.docx] _(420 kB)_ [^TemenosBNKReleaseDocStewie3rdAug2020.docx] _(420 kB)_', groupName='null', roleLevelId=null, issue=GCD-762, commentProperties={exalate.comment.creator={"byExalate":true}}, createNotification=false}` The Service Desk Error: `null`, message `You don't have permission to access this Service Desk.`

    CommentAdd your comment...

    2 answers

    1.  
      1
      0
      -1

      Hi.


      Incoming sync quite long (I had to reduct it a bit). It all works, just the permission is the issue.


      if(firstSync){
         // issue.projectKey   = "GCD"
         // Set type name from source issue, if not found set a default
         // issue.typeName     = nodeHelper.getIssueType(replica.typeName)?.name ?: "Task"
         if(replica.type.name == "Admin Reporting") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "COB") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Data Upload") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Decommission Server") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Delivery Admin") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Dev/Test Setup") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Documentation") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "DR Setup") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "ETL") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "File Request") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Go-Live Checklist") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Incident") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Integration") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "License Update") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }   
         else if (replica.type.name == "Network Request") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Non-Prod Setup") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Presales") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Prod Setup") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Restart Services") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Risk") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Security Task") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Server Spec") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Task") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Tuning") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Upgrades / Monthly Builds") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Task"
         }
         else if (replica.type.name == "Environment Refresh") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Environment Refresh"
         }
         else if (replica.type.name == "Backup – Appl. & DB") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Backup - Appl. & DB Data"
         }
         else if (replica.type.name == "Release") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Release"
         }
         else if (replica.type.name == "Sub-task") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Sub-task"
         }
         else if (replica.type.name == "User Access") {
             issue.projectKey   = "GCD"
             issue.typeName     = "User Access"
         }
         else if (replica.type.name == "Change Request") {
             issue.projectKey   = "GCD"
             issue.typeName     = "Change Request"
         }
        
        // set date when the issue is created during sync
        // issue.customFields."Migration Date".value = "Date"
      }
      
      
      if(firstSync && replica.parentId){
          issue.typeName     = "Sub-task" //Make sure to use the right subtask type here.
          def localParent = nodeHelper.getLocalIssueFromRemoteId(replica.parentId.toLong())
          if(localParent){
              issue.parentId = localParent.id
          } else {
             throw new com.exalate.api.exception.IssueTrackerException("Subtask cannot be created: parent issue with remote id " + replica.parentId + " was not found. Please make sure the parent issue is synchronized before resolving this error" )
          }
      }
      
      
      // Organizations sync
      
      //add organization names into the Text Field
      issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n"+ "Organizations: " + replica.customKeys."Organization Names"?.join(",")
      
      // Assing Organization to variable that it can be processed in if else list later. Assuming that only 1 organization exists per ticket
      def orgName = replica.customKeys."Organization Names"?.join(",")
      
      if (orgName == "BNE"){
          issue.customFields.Organizations.value = "GCD-Alex Technologies Pty Ltd"
      }
      
      
      
      //CRM Company assign based on Customer (Cloud Imp)
      if (orgName.isEmpty()) {
          
          def customerCloud1 = replica.customFields."Customer (Cloud Imp)".value.value
          
          def customerCloud = customerCloud1?.join("")
          
          issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n" + "orgName is emty"
          issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n" + "Customer (Cloud Imp): " + customerCloud?.join("")
          
          if (customerCloud == "BNE"){
              issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n" + "if condition for BNE executed"
              issue.customFields."CRM Company".value = "XXX"
          }
          
      }
      
      
      // Customer Request type
      //if (replica.key == "CD-9160"){
          // def customerRequestType = replica.customFields."Customer Request Type".value
      //}
      
      def issueType = replica.type.name
      
      if (issueType == "Release"){
          issue.customFields."Customer Request Type".value = "Release/Change"
      }
      else if (issueType == "Documentation"){
          issue.customFields."Customer Request Type".value = "Documentation"
      }
      else if (issueType == "Incident"){
          issue.customFields."Customer Request Type".value = "Incident"
      }
      else if (issueType == "User Access"){
          issue.customFields."Customer Request Type".value = "User Access"
      }
      else if (issueType == "Restart Services"){
          issue.customFields."Customer Request Type".value = "Restart Services"
      }
      else if (issueType == "Integration"){
          issue.customFields."Customer Request Type".value = "Integration"
      }
      else if (issueType == "File Request"){
          issue.customFields."Customer Request Type".value = "Data Extract - Files / Logs"
      }
      else if (issueType == "Data Upload"){
          issue.customFields."Customer Request Type".value = "Data Upload"
      }
      else if (issueType == "Environment Refresh"){
          issue.customFields."Customer Request Type".value = "Environment Refresh"
      }
      else if (issueType == "Backup - Appl. & DB Data"){
          issue.customFields."Customer Request Type".value = "Backup - Appl. & DB Data"
      }
      else if (issueType == "COB"){
          issue.customFields."Customer Request Type".value = "COB"
      }
      else if (issueType == "ETL"){
          issue.customFields."Customer Request Type".value = "ETL"
      }
      else if (issueType == "Network Reqeust"){
          issue.customFields."Customer Request Type".value = "Network Changes"
      }
      else if (issueType == "Change Request"){
          issue.customFields."Customer Request Type".value = "Change Request (CR)"
      }
      else if (issueType == "Tuning"){
          issue.customFields."Customer Request Type".value = "Tuning"
      }
      else if (issueType == "Decomission Server"){
          issue.customFields."Customer Request Type".value = "Decomission Server"
      }
      else if (issueType == "Admin Reporting"){
          issue.customFields."Customer Request Type".value = "Delivery Reporting"
      }
      else if (issueType == "Risk"){
          issue.customFields."Customer Request Type".value = "Risk"
      }
      else if (issueType == "Dev/Test Setup"){
          issue.customFields."Customer Request Type".value = "Dev Test Setup"
      }
      else if (issueType == "Task"){
          issue.customFields."Customer Request Type".value = "Miscellaneous Request"
      }
      else if (issueType == "Prod Setup"){
          issue.customFields."Customer Request Type".value = "Prod Setup"
      }
      else if (issueType == "DR Setup"){
          issue.customFields."Customer Request Type".value = "DR Setup"
      }
      else if (issueType == "Delivery Admin"){
          issue.customFields."Customer Request Type".value = "Delivery Admin Setup"
      }
      else if (issueType == "Non-Prod Setup"){
          issue.customFields."Customer Request Type".value = "Non-Prod Setup"
      }
      else if (issueType == "License Update"){
          issue.customFields."Customer Request Type".value = "License Update"
      }
      else if (issueType == "Upgrades / Monthly Builds"){
          issue.customFields."Customer Request Type".value = "Upgrades / Monthly Builds"
      }
      else if (issueType == "Presales"){
          issue.customFields."Customer Request Type".value = "Presales"
      }
      else if (issueType == "Go-Live Checklist"){
          issue.customFields."Customer Request Type".value = "Go-Live Checklist"
      }
      
      //issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n"+ "Customer Request Type: " + customerRequestType
      issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n"+ "Issue Type: " + issueType
      
      //issue.customFields."Customer Request Type".value =  replica.customFields."Customer Request Type"
      
      
      // System fields
      issue.summary      = replica.summary
      issue.description  = replica.description
      issue.labels       = replica.labels
      issue.created      = replica.created
      issue.resolutiondate    = replica.resolutiondate
      issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)
      
      // User Synchronization (Assignee/Reporter)
      
      // Set a Reporter/Assignee from the source side, if the user can't be found set a default user
      // You can use this approach for custom fields of type User
      //def defaultUser = nodeHelper.getUserByEmail("jirabot_service@temenos.com")
      issue.creator = nodeHelper.getUserByUsername(replica.creator?.username)
      issue.assignee = nodeHelper.getUserByUsername(replica.assignee?.username)
      issue.reporter = nodeHelper.getUserByUsername(replica.reporter?.username)
      
      // Requested Participants
      issue.customFields."Request participants"?.value = replica.customFields."Request participants"?.value?.collect { it ->
          nodeHelper.getUserByEmail(it?.email) 
      }
      
       
      /*
      Comment Synchronization
      
      Sync comments with the original author if the user exists in the local instance
      Remove original Comments sync line if you are using this approach
      */
      issue.comments = commentHelper.mergeComments(issue, replica){ it.executor = nodeHelper.getUserByEmail(it.author?.email) }
      //issue.comments     = commentHelper.mergeComments(issue, replica)
       
      /*
      Status Synchronization
      
      Sync status according to the mapping [remote issue status: local issue status]
      If statuses are the same on both sides don't include them in the mapping
      def statusMapping = ["Open":"New", "To Do":"Backlog"]
      def remoteStatusName = replica.status.name
      issue.setStatus(statusMapping[remoteStatusName] ?: remoteStatusName)
      */
      def statusMap = [
       
             // "remote status name": "local status name"
               "Closed" : "Closed",
               "Paused" : "Paused",
               "Triage" : "Triage",
               "Waiting for Customer" : "Waiting for Customer",
               "In Progress"  :   "In Progress",
               "Waiting for Internal 3rd Party"   : "Waiting for Internal 3rd Party",
               "Pending"  :   "Pending",
               "Awaiting approval"    :   "Awaiting Approval",
               "Estimation"   :   "Estimate",
               "Remediating"  :   "In Progress",
               "Scoping"  :   "Scoping"
         ]
      def remoteStatusName = replica.status.name
      issue.setStatus(statusMap[remoteStatusName] ?: remoteStatusName)
      
      // Priority Mapping
      def priorityMapping = [
              // remote side priority <-> local side priority            
                "Blocker" : "Blocker",
                "Critical" : "Critical",
                "Major" : "Medium",
                "Minor" : "Low",
                "Trivial" : "Minor"
          ]
      def priorityName = priorityMapping[replica.priority?.name] ?: "Low" // set default priority in case the proper urgency could not be found
      issue.priority = nodeHelper.getPriority(priorityName)
      
      // Resolution Mapping
      if (replica.resolution == null && issue.resolution != null) {
         // if the remote issue is not resolved, but the local issue is set, then clear the local issue resolution
        
         issue.resolution = null
      }
        
      if (replica.resolution != null) {
         // the remote issue is resolved, but the local isn't - look up the correct local resolution object.
       
       
         def resolutionMap = [
              "Cannot Reproduce" : "Cannot Reproduce",
              "Current Production Issue" : "Known Error",
              "Data Integrity" :  "Code Fix",
              "Duplicate" : "Duplicate",
              "Fixed" : "Resolved",
              "Incomplete" : "Incomplete",
              "Infrastructure/Environmental" : "Cannot Reproduce",
              "Knowledge/Training" : "Working as Expected",
              "Out of Scope Requirement" : "Feature Request",
              "Raised in Error" : "Declined",
              "Request Completed" : "Resolved",
              "Will not be actioned" : "Won't Do",
              "Done" : "Done",
              "Won't Do" : "Won't Do",
              "Declined" : "Declined"
         ]
        
         // use 'done' as resolution if the remote resolution is not found
         def targetResolutionName = resolutionMap[replica.resolution.name] ?: "Done"
          
         // nodeHelper.getResolution looks up the local resolution object based on the provided name
         issue.resolution = nodeHelper.getResolution(targetResolutionName)
      }
      
      
      /*
      Custom Fields
      
      This line will sync Text, Option(s), Number, Date, Organization, and Labels CFs
      For other types of CF check documentation
      issue.customFields."CF Name".value = replica.customFields."CF Name".value
      */
      issue.customFields."Migration Ticket".value = replica.key
      issue.customFields."Migration Ticket URL".value = "https://client.rubik.com.au/support/browse/" + replica.key
      issue.customFields."Migration Instance".value = "Rubik"
      
      // single selects
      if(replica.customFields."Request Type"?.value){
        issue.customFields."User Access Request Type".value = replica.customFields."Request Type".value.value
      }
      if(replica.customFields."Team Responsible"?.value){
        issue.customFields."Team Responsible".value = replica.customFields."Team Responsible".value.value
      }
      if(replica.customFields."Cloud Infrastructure"?.value){
        issue.customFields."Cloud Infrastructure".value = replica.customFields."Cloud Infrastructure".value.value
      }
      if(replica.customFields."User Access Type"?.value){
        issue.customFields."User Access Type".value = replica.customFields."User Access Type".value.value
      }
      if(replica.customFields."Hosting Solution"?.value){
        issue.customFields."Hosting Solution".value = replica.customFields."Hosting Solution".value.value
      }
      if(replica.customFields."CR Classification"?.value){
          issue.customFields."CR Classification".value = replica.customFields."CR Classification".value.value
      }
      if(replica.customFields."CR Type"?.value){
          issue.customFields."CR Type" = replica.customFields."CR Type"
      }
      
      // radio
      if(replica.customFields."Cloud Security Policy Acknowledgement"?.value){
          issue.customFields."Cloud Security Policy Acknowledgement".value = replica.customFields."Cloud Security Policy Acknowledgement".value.value
      }
      if(replica.customFields."CR Outcome"?.value){
          issue.customFields."CR Outcome" = replica.customFields."CR Outcome"
      }
      
      
      //dates
      issue.due = replica.due
      issue.customFields."Rollback Date".value = replica.customFields."Rollback Date".value
      
      
      // Checkboxes
      def checkboxCollection1 = replica.customFields."Env Deployment Checklist".
                                      value?.
                                      collect{
                                              a->
                                              nodeHelper.getOption (issue, "Env Deployment Checklist", a.value)
                                              }
      issue.customFields."Env Deployment Checklist".value = checkboxCollection1
      
      def checkboxCollection2 = replica.customFields."Env Refresh Checklist".
                                      value?.
                                      collect{
                                              a->
                                              nodeHelper.getOption (issue, "Env Refresh Checklist", a.value)
                                              }
      issue.customFields."Env Refresh Checklist".value = checkboxCollection2
      
      def checkboxCollection3 = replica.customFields."Env Verification Checklist".
                                      value?.
                                      collect{
                                              a->
                                              nodeHelper.getOption (issue, "Env Verification Checklist", a.value)
                                              }
      issue.customFields."Env Verification Checklist".value = checkboxCollection3
      
      
      def checkboxCollection4 = replica.customFields."Environment".
                                      value?.
                                      collect{
                                              a->
                                              nodeHelper.getOption (issue, "Cloud Environment", a.value)
                                              }
      issue.customFields."Cloud Environment".value = checkboxCollection4
      // issue.customFields."Migration Details".value = issue.customFields."Migration Details".value +"\n" + checkboxCollection4?.join(",")
      
      
      def checkboxCollection5 = replica.customFields."Product/s".
                                      value?.
                                      collect{
                                              a->
                                              nodeHelper.getOption (issue, "Migration Products", a.value)
                                              }
      issue.customFields."Migration Products".value = checkboxCollection5
      
      
      
      
      //Text single (ensure that field names are correct. Its case sensitive.)
      if(replica.customFields."Contact Email"?.value){
          issue.customFields."Contact Email".value = replica.customFields."Contact Email".value
      }
      if(replica.customFields."Contact Person"?.value){
          issue.customFields."Contact Person".value = replica.customFields."Contact Person".value
      }
      if(replica.customFields."Job Title"?.value){
          issue.customFields."Job Title".value = replica.customFields."Job Title".value
      }
      if(replica.customFields."Pack Location"?.value){
          issue.customFields."Pack Location".value = replica.customFields."Pack Location".value
      }
      if(replica.customFields."Refresh From"?.value){
          issue.customFields."Refresh From".value = replica.customFields."Refresh From".value
      }
      if(replica.customFields."Refresh To"?.value){
          issue.customFields."Refresh To".value = replica.customFields."Refresh To".value
      }
      if(replica.customFields."Team Name"?.value){
          issue.customFields."Team Name".value = replica.customFields."Team Name".value
      }
      
      
      
      //Text field to Number field (text field must have a digit)
      if(replica.customFields."Estimate - High Level (days)"?.value){
          def valueStr = replica.customFields."Estimate - High Level (days)".value
          double valueNum = Double.valueOf(valueStr)
          issue.customFields."Est. (Days)".value = valueNum
      }
      
      
      //Test multiline
      if(replica.customFields."Server IPs"?.value){
          issue.customFields."Server IPs".value = replica.customFields."Server IPs".value
      }
      
      
      // Radion to Cascading field
      if(replica.customFields."Release Window"?.value){
          def radioValue = replica.customFields."Release Window".value.value
          
          issue.customFields."Release Window".value = nodeHelper.getCascadingSelect(
             nodeHelper.getOption(issue, "Release Window", "APAC"),  // hardcoded value
             nodeHelper.getOption(issue, "Release Window", radioValue )
             )
      }
      
      
      
      
      //other
      // issue.customFields."Approvers" = replica.customFields."Approvers"
      
      
      1. Francis Martens (Exalate)

        It is indeed long.  You might have a look at the switch statement in groovy which would help compress the incoming sync.

        Could you please remove the line


        issue.creator = nodeHelper.getUserByUsername(replica.creator?.username)
        
        


        From the incoming sync script and try again.

        Also as a suggestion you can also ensure that the reporter and assignee are always set with following code


        def defaultUser = nodeHelper.getUserByEmail("jirabot_service@temenos.com")
        issue.assignee = nodeHelper.getUserByUsername(replica.assignee?.username) ?: defaultUser
        issue.reporter = nodeHelper.getUserByUsername(replica.reporter?.username) ?: defaultUser
      CommentAdd your comment...
    2.  
      1
      0
      -1

      Hi Jiri Kanicky


      Thank you for raising this question here - much obliged.
      I tried to reproduce the issue but failed (ie it works here)


      What I did is in the incoming sync on my 'GCD', I added following line of code

      if (firstSync) {
         issue.reporter = nodeHelper.getUserByEmail("customer@customer.com")
      
      }



      The permission helper confirmed that this user has no create permissions






      Still the ticket gets created




      What is your current 'incoming sync processor'?


        CommentAdd your comment...