tcplugins/tcWebHooks

Escaping ${}

EGBland opened this issue · 6 comments

Microsoft Adaptive Cards support data contexts and data binding, via the same variable syntax that tcWebHooks uses (dollar sign and braces). Is there a way to escape these characters in tcWebHooks so they're inserted as literal characters in the generated JSON body?

Hi @EGBland
There is a section in the docs about the Velocity templating engine regarding escaping.

If you're using the teams template you might need to convert it to use Velocity.

I did some testing and came up with the following...

Velocity Template

#set ( $prefix = '${' )
#set ( $suffix = '}' )

{ 
    "tests referring to variables that are not valid": {
        "valid variable": "${test}",
        "non existant variable": "$test2",
        "non existant variable": "${test3}"
    },
    
    "tests with actual variables" : {   
        "buildResult" : "${buildResult}"
        "buildResultEscaped" : "\${buildResult}"
        "buildResultDoubleEscaped" : "\\${buildResult}"
    },
    
    "tests using variable as prefix and suffix" : {
        "fred" : "${prefix}fred${suffix}",
        "buildResult" : "${prefix}${buildResult}${suffix}" 
    },
    
    "unexpected results with prefix and suffix" : {
        "there is a variable called test defined in the build" : "${test}",
        "having a space in the variable works" : "${prefix} test ${suffix}", 
        "no space around the variable resolves it for some reason" : "${prefix}test${suffix}"
    }
}

Output from previewing the template with a build

{ 
    "tests referring to variables that are not valid": {
        "valid variable": "test value123",
        "non existant variable": "$test2",
        "non existant variable": "${test3}"
    },
    
    "tests with actual variables" : {   
        "buildResult" : "success"
        "buildResultEscaped" : "success"
        "buildResultDoubleEscaped" : "\success"
    },
    
    "tests using variable as prefix and suffix" : {
        "fred" : "${fred}",
        "buildResult" : "${success}" 
    },
    
    "unexpected results with prefix and suffix" : {
        "there is a variable called test defined in the build" : "test value123",
        "having a space in the variable works" : "${ test }", 
        "no space around the variable resolves it for some reason" : "test value123"
    }
}

Does that help you at all?

I hadn't considered using the Velocity template; I was hoping there'd be a simpler way to do it with the plainer templates.

No worries though, I'll look into the Velocity template and see if I can solve this problem with that.

Thanks for the fast response!

The old template engine was written by me about 10 years ago, and is fairly limited. That's why I recommend updating to Velocity.

I can help you convert the template if you want to post it here, or let me know if you're using one of the bundled ones (eg, Teams).

Instead of using data contexts in Adaptive Cards, I opted to instead generate the body with Velocity. My solution follows.

I think I've got most of it sorted now; if you could tell me what to add to the ${buildStatusUrl} to get it to point at the changes tab on a build's status, I would be grateful :-)

{
  "type": "message",
  "summary": "TeamCity - Build Successful - ${buildName}",
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "contentUrl": null,
      "content": {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.2",
        "body": [
          {
            "type": "TextBlock",
            "text": "Build Successful",
            "size": "extraLarge",
            "weight": "bolder",
            "color": "good"
          },
          {
            "type": "FactSet",
            "facts": [
              {
                "title": "Project",
                "value": "${buildFullName}"
              },
              {
                "title": "Build number",
                "value": "${buildNumber}"
              },
              {
                "title": "Agent",
                "value": "${agentName} / ${agentOs}"
              },
              {
                "title": "Triggered by",
                "value": "${triggeredBy}"
              }
            ]
          },
          {
            "type": "TextBlock",
            "text": "Changes",
            "size": "large"
          },
          #foreach( $change in $changes )
          {
            "type": "TextBlock",
            "text": "${change.version}: (${change.change.username}) ${change.change.comment}"
          },
          #end
        ],
        "actions": [
          {
            "type": "Action.OpenUrl",
            "title": "View changes in TeamCity",
            "url": "${buildStatusSakuraUrl}?buildTab=changes"
          }
        ]
      }
    }
  ]
}

oh dear, it looks like I added the variable, but never added to the editor. There is a new variable called buildStatusSakuraUrl because the new(ish) React UI is called Sakura.

So you can change url block like this...

            "url": "${buildStatusSakuraUrl}?buildTab=changes"

Your provided URL block works perfectly, thank you for the help! :-)

I've edited my previous comment with the change.