When projects are created by Exalate, a standard list of custom fields should be added to those projects on firstSync

Hi,

When ADO creates a project in Asana I need it to automatically create the custom fields for the project too. This Asana incoming is not currently doing the trick. (Dubby GIDs used) please let me know if you can help:

Asana incoming:
if (replica.summary) {
// First decode HTML entities
def decodedSummary = replica.summary
.replaceAll(‘&’, ‘&’)
.replaceAll(‘<’, ‘<’)
.replaceAll(‘>’, ‘>’)
.replaceAll(‘"’, ‘"’)
.replaceAll(‘’, “'”)
.replaceAll(‘:’, “:”)

// Replace invalid characters with space, then clean up multiple spaces
entity.summary = decodedSummary
    .replaceAll('[^a-zA-Z0-9\\s\\-_():.]', ' ')  // Added period, more permissive
    .replaceAll('\\s+', ' ')                      // Collapse multiple spaces
    .trim()                                       // Remove leading/trailing spaces

// Ensure summary is not empty after sanitization
if (!entity.summary) {
    entity.summary = "Untitled Task"
}

}

if (firstSync) {
if (replica.type?.name == “Dev Spike” || replica.type?.name == “User Story” || replica.type?.name == “Bug”){
entity.description = replica.description ? nodeHelper.convertToAsanaHtml(replica.description, true) : null
entity.type = nodeHelper.getIssueType(“task”)
}
else if (replica.type?.name == “Feature”){
entity.description = replica.description ? nodeHelper.convertToAsanaHtml(replica.description, true) : null
entity.type = nodeHelper.getIssueType(“task”)
if (entity.customFields.milestone) {
entity.customFields.milestone.value = true
}
}
else if (replica.type?.name == “Epic”){
entity.description = replica.description ? nodeHelper.convertToAsanaHtml(replica.description, true) : null
entity.type = nodeHelper.getIssueType(“project”)
store(entity)

    // Add project to team
    httpClient.put("/api/1.0/projects/${entity.key}", """
        {"data":[{"team_gid":"1209616298303531"}]}
    """)
    
    // Add custom fields to project 
    def customFieldGids = [
        "12108948692-----",      //  Iteration
        "12108948714------",      // Area Path
        "12108949207-----",      // ID
        "12108949196-----",      // State
        "12108965505-----",      // Product            
        "12108872901-----",      // Release Assignment            
        "12108884875-----",      // Story Points           
        "12108884875-----",      // Tshirt          
        "12108698815-----",      // ADO - ReleaseStatus            
        "121178749170-----",      // Tags
        "12108872901-----"       // Priority
    ]
    
    customFieldGids.each { fieldGid ->
        try {
            httpClient.post("/api/1.0/projects/${entity.key}/addCustomFieldSetting", """
                {"data":{"custom_field":"${fieldGid}"}}
            """)
        } catch (Exception e) {
            // Log but don't fail if field already exists
        }
    }
}

}
/*
try {
entity.customFields.“ADO - State”.value = replica.status?.name
entity.customFields.“ADO - Status”.value = replica.status?.name

} catch (Exception e) {
// Skip if status value is not a valid Asana enum option
}
*/
//Asana Fields Sync
entity.customFields.“ADO Story Points”.value = replica?.“Microsoft.VSTS.Scheduling.StoryPoints”

entity.customFields.“ADO - Priority”.value = replica.priority?.name
entity.customFields.“ADO - ID”.value = replica.key
entity.customFields.“ADO - Area Path”.value = replica.areaPath
entity.customFields.“ADO - Iteration”.value = replica.iterationPath
entity.customFields.“ADO - Product”.value = replica.customFields.“Product”?.value
entity.customFields.“ADO - Tags”.value = replica.“System.Tags”
entity.customFields.“ADO - ReleaseStatus”.value = replica.customFields.“Release Status”?.value
entity.customFields.“ADO - TShirtSizing” = replica.customFields.“TShirt”
entity.customFields.“ADO - ReleaseAssignment”.value = replica.customFields.“Release Assignment”?.value
entity.customFields.“ADO - BacklogPriority”.value = replica.customFields.“Backlog Prioritization”?.value

entity.labels = replica.labels
entity.description = nodeHelper.convertToAsanaHtml(replica.description, true)

entity.comments = commentHelper.mergeComments(entity, replica, {
comment →
comment.body = comment.author.displayName + " commented: " + comment.body
comment.body = nodeHelper.convertToAsanaHtml(comment.body)
comment
})

if (replica.type.name == “Dev Spike” || replica.type.name == “User Story” || replica.type.name == “Bug”){
if (replica.parentId) {
def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.parentId.toLong(), “issue”)
if (localParent) {
entity.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 first.”
)
}
}
}
else if (replica.type.name == “Feature”){
if (replica.parentId) {
def localParent = syncHelper.getLocalIssueKeyFromRemoteId(replica.parentId.toLong(), “issue”)
if (localParent) {
entity.customFields.project.value = localParent.id
} else {
throw new com.exalate.api.exception.IssueTrackerException(
“Feature cannot be created: parent issue with remote id " + replica.parentId +
" was not found. Please make sure the parent issue is synchronized first.”
)
}
}
}

Hi Micah,

Good to see you here!

I researched this issue and the recommendation from Asana in such cases is to create the projects using a template. This feature is already supported via Exalate and it is the cleanest solution I think.

Your script can be greatly simplified as a result of this as well e.g.
entity.customFields.“asana_template_gid”.value = “1212937838763287”
entity.type = nodeHelper.getIssueType(“project”)
should be enough to get the project created with the correct set of custom fields from the template.

In order to do this, please create a Porject Template with the correct fields added, and we should be able to quickly implement this in our next meeting.

Thanks
Majid

Thanks for joining the call the other day.
We were able to test that by creating a project using a template, the custom fields came up. But you ran into some issues at the end of the call. Wondering if you have tested it further.

Thanks