tcplugins / tcWebHooks

WebHooks plugin for Teamcity. Supports many build states and payload formats.
https://netwolfuk.wordpress.com/category/teamcity/tcplugins/tcwebhooks/
155 stars 30 forks source link

Escaping ${} #247

Closed EGBland closed 1 month ago

EGBland commented 1 month ago

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?

netwolfuk commented 1 month ago

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?

EGBland commented 1 month ago

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!

netwolfuk commented 1 month ago

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).

EGBland commented 1 month ago

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"
          }
        ]
      }
    }
  ]
}
netwolfuk commented 1 month ago

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"
EGBland commented 1 month ago

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

I've edited my previous comment with the change.