This week, a couple of people asked (forum post 1 and forum post 2) how to subscribe to the CheckinEvent notification when there's a checkin under a particular tree.  The first person wanted to filter by path in order to kick off continuous integration.  The second person wanted to filter by path for email subscriptions.

Here's the command line that I used to configure an email notification for a path.  I've changed the email address (someone@microsoft.com), the server name (http://MyVstfat01:8080), and the path ($/TeamProject/A) from their original values, but it's otherwise what I've used to create a successful subscription.

bissubscribe /eventType CheckinEvent /address someone@domain.com /deliveryType EmailHtml /server http://MyVstfat01:8080 /filter "'Artifacts/Artifact[starts-with(@ServerItem, \"$/TeamProject/A\")]' <> null"

The difference for CI would be the delivery type and address.  With that changed, the following should work (I confess I didn't try this to verify it, but it's based on the subscription in Jeff Atwood's continuous integration post).

bissubscribe /eventType CheckinEvent /address http://tsweb01:8080/ci/notify.asmx /deliveryType Soap /server http://MyVstfat01:8080 /filter "'Artifacts/Artifact[starts-with(@ServerItem, \"$/TeamProject/A\")]' <> null"

However, the expressions that I just wrote won't work reliably.  Why? 

Unfortunately, using server path comparisons is not as simple as it should be.  What happens is that the version control server uses the workspace mappings of the user checking in the changes to construct the paths.  So if the person checking in $/teamproject/foo/bar.cs uses a server path of $/TeAmPrOjEcT/Foo mapped to c:\project\foo, then the path that will be compared in the filter expression will be $/TeAmPrOjEcT/Foo/bar.cs.

Why is this a problem since the version control server is case insensitive?  Well, the problem is that the filter expressions are evaluated using XPath, and XPath is case sensitive.

So, version control constructs server paths used in checking in changes by using the user's workspace mappings, XPath is case sensitive, and not every user types in the paths the same way when creating workspace mappings (e.g., $/MyProject/Foo vs. $/myproject/foo).  The result is that when using the expressions above you'll only get notifications when the user checking in uses the same casing that you do for the paths.

Fortunately, XPath 1.0, which is what the server uses, provides the translate() function.  The translate() function will convert characters in the first string to the corresponding characters in the second string.  For example, translate("$/A/b/C", "ABC", "abc") will return "$/a/b/c". You can read more about it in the XPath 1.0 documentation.

So, that means the filter expressions must get more complex in order to work in all circumstances.

bissubscribe /eventType CheckinEvent /address someone@domain.com /deliveryType EmailHtml /server http://yourserver:8080 /filter "'Artifacts/Artifact[starts-with(translate(@ServerItem, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", \"abcdefghijklmnopqrstuvwxyz\"), \"$/teamproject/a\")]' <> null"

Here's how it works.  It still uses the XPath starts-with() function, but the first argument to that function is now the output of the translate() function.  The translate() call takes all upper case letters in the English alphabet and converts them to the corresponding lower case letters in the input string from the server, @ServerItem.  In the original filter expression, I specified $/TeamProject/A.  However, now that everything is being comverted to lower case, it must be written as "$/teamproject/a" in order to work.

You would need to make a similar adjustmet to the SOAP subscription as well.

If you want to be notified of all checkins for $/teamproject/a where the files have some particular file extension, such as .resx, you would need to use a substring and string-length in the expression as follows.

bissubscribe /eventType CheckinEvent /address someone@domain.com  /server http://yourserver:8080 /deliveryType EmailHtml /filter "'Artifacts/Artifact[starts-with(translate(@ServerItem, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", \"abcdefghijklmnopqrstuvwxyz\"), \"$/teamproject/a\")]' <> null AND 'Artifacts/Artifact[substring(translate(@ServerItem, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\", \"abcdefghijklmnopqrstuvwxyz\"), 1 + string-length(@ServerItem) - 5) = \".resx\"]' <> null"

Naren Datha posted a nice summary of links for information on subscribing to events.

NOTE:  There's now a subscription editor in the Team Foundation Power Tools (July '08 and newer) that Naren wrote.  Look for a new Alerts node in Team Explorer under the team project.  I highly recommend it!

[UPDATE]  I should also have mentioned that for those of you trying to filter the builds, check out Filtering the builds listed in work item tracking.

[UPDATE 2/2/08]  I've had to change this post significantly to explain how to create subscriptions with filters that will work reliably because XPath is case sensitive, and the version control server does not force a consistent casing of the paths in the changeset.

[UPDATE 8/22/08]  Added an example of getting notifications for files with a particular extension and added a note about the new alerts editor in the latest power tools release.

[UPDATE 1/03/09]  Added slashes to escape the unescaped quotation marks in the last bissubscribe call.

tags: , , ,