gregpakes
gregpakes

Reputation: 4535

How to get ServiceStack to receive SOAP requests?

I am trying to use TFS build notifications to alert an endpoint in my ServiceStack service. TFS uses SOAP and I have little or no experience with SOAP.

Whenever TFS hits the endpoint, all the properties are null in my Request DTO.

My code is as follows:

DTO

[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
[DataContract(Namespace = "", Name = "BuildStatusChangeEvent")]
public class TfsEventBuildComplete : IReturn<BuildCompleteResponse>
{
    [DataMember(Order = 1)]
    public string BuildUri { get; set; }

    [DataMember(Order = 2)]
    public string TeamFoundationServerUrl { get; set; }

    [DataMember(Order = 3)]
    public string TeamProject { get; set; }

    [DataMember(Order = 4)]
    public string Title { get; set; }

    [DataMember(Order = 5)]
    public string Id { get; set; }

    [DataMember(Order = 6)]
    public TfsEventStatusChange StatusChange { get; set; }

    [DataMember(Order = 7)]
    public string ChangedBy { get; set; }
}

public class TfsEventStatusChange
{
    public string FieldName { get; set; }

    public string NewValue { get; set; }
}

I am subscribing to the BuildCompletionEvent within TFS. If I send the raw XML that I know TFS is sending to the endpoint, all works well. But when it is packaged up as a SOAP request, it doesn't work. The XML is enclosed:

<BuildStatusChangeEvent 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"     
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <BuildUri>vstfs:///Build/Build/4799</BuildUri>
    <TeamFoundationServerUrl>http://TFSURL:8080/tfs/collection</TeamFoundationServerUrl>
    <TeamProject>iSAMS</TeamProject>
    <Title>iSAMS Build Release_20130814.1 Quality Changed To Publish</Title>
    <Subscriber>[02]\Project Collection Service Accounts</Subscriber>
    <Id>Release_20130814.1</Id>
    <Url>http://TFSURL:8080/tfs/web/build.aspx?pcguid=GUID&amp;builduri=vstfs:///Build/Build/4799</Url>
    <TimeZone>GMT Daylight Time</TimeZone>
    <TimeZoneOffset>+01:00:00</TimeZoneOffset>
    <ChangedTime>8/14/2013 9:33:30 AM</ChangedTime>
    <StatusChange>
        <FieldName>Quality</FieldName>
        <NewValue>Publish</NewValue>
    </StatusChange>
    <ChangedBy>DOMAIN\USER</ChangedBy>
</BuildStatusChangeEvent>

Update

I have managed to get the SOAP request and it looks like this:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
    <s:Header xmlns:w="http://www.w3.org/2005/08/addressing">
        <w:Action>http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify</w:Action>
        <w:To>http://192.168.232.51/ErrorReporting.Web/releasemanagement/tfsevents/buildcomplete</w:To>
</s:Header>
<s:Body>
    <Notify xmlns="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03">
        <eventXml>&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;BuildCompletionEvent xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;&lt;BuildUri&gt;vstfs:///Build/Build/4818&lt;/BuildUri&gt;&lt;TeamFoundationServerUrl&gt;http://tfsdomain:8080/tfs/02&lt;/TeamFoundationServerUrl&gt;&lt;TeamProject&gt;projectname&lt;/TeamProject&gt;&lt;Id&gt;Test_20130815.10&lt;/Id&gt;&lt;Url&gt;http://tfsdomain:8080/tfs/web/build.aspx?pcguid=88661c27-0916-48cb-8e39-9b40d8beb457&amp;amp;builduri=vstfs:///Build/Build/4818&lt;/Url&gt;&lt;Title&gt;Build Test_20130815.10 Successfully Completed&lt;/Title&gt;&lt;CompletionStatus&gt;Successfully Completed&lt;/CompletionStatus&gt;&lt;Subscriber&gt;TFSSERVICE&lt;/Subscriber&gt;&lt;Configuration&gt;Test&lt;/Configuration&gt;&lt;RequestedBy&gt;domain\\username&lt;/RequestedBy&gt;&lt;TimeZone&gt;GMT Daylight Time&lt;/TimeZone&gt;&lt;TimeZoneOffset&gt;+01:00:00&lt;/TimeZoneOffset&gt;&lt;BuildStartTime&gt;8/15/2013 3:38:48 PM&lt;/BuildStartTime&gt;&lt;BuildCompleteTime&gt;8/15/2013 3:39:11 PM&lt;/BuildCompleteTime&gt;&lt;BuildMachine&gt;Master - Controller&lt;/BuildMachine&gt;&lt;/BuildCompletionEvent&gt;</eventXml>
        <tfsIdentityXml>&lt;TeamFoundationServer url="http://isams-tfs2012:8080/tfs/02/Services/v3.0/LocationService.asmx" /&gt;</tfsIdentityXml>
        <SubscriptionInfo>
            <Classification>&lt;PT N="management.domain.co.uk - Release build subscription." /&gt;</Classification>
            <ID>274</ID>
            <Subscriber>S-1-5-21-3680174039-3422584590-495469403-1397</Subscriber>
        </SubscriptionInfo>
    </Notify>
</s:Body>
</s:Envelope>

I have also tried changing my DTO to:

[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
[DataContract(Namespace = "", Name = "Notify")]
public class TfsEventBuildCompleteSoap
{
    [DataMember(Order = 1)]
    public string eventXml { get; set; }

    [DataMember(Order = 2)]
    public string tfsIdentityXml { get; set; }

    [DataMember(Order = 3)]
    public SubscriptionInfo SubscriptionInfo { get; set; }
}

[Serializable]
public class SubscriptionInfo
{
    public string Classification { get; set; }

    public int ID { get; set; }

    public string Subscriber { get; set; }
}

But I am still not having any joy.


Update 2

I have also tried the following:

[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
public class TfsBuildEvent : IRequiresSoapMessage
{
    public Message Message { get; set; }
}

But Message is always null too.

I have also tried using the Request.GetSoapMessage(), but this gives the error:

given key was not present in the dictionary

Upvotes: 1

Views: 823

Answers (1)

gregpakes
gregpakes

Reputation: 4535

I have finally got this working.

My end setup is as follows:

DTO

[Route("/releasemanagement/tfsevents/buildcomplete", "POST")]
[DataContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03", Name = "Notify")]
public class Notify : IReturn<BuildCompleteResponse>
{
    [DataMember(Order = 1)]
    public string eventXml { get; set; }

    [DataMember(Order = 2)]
    public string tfsIdentityXml { get; set; }
}

I was also posting my request to the wrong URL. I was posting it to the URL defined in the Route attribute. But I shoud have been posting to /soap12.

Important

The class name was required to match the opening element name in the soap body. The Name attribute on DataContract does not do this for you. My original classname was TfsEventBuildCompleteSoap, but it did not start working until I renamed the class Notify.

This blog post explains it in more detail

Upvotes: 3

Related Questions