Subject: | SOAP-Server validation rejects elements if 'elementFormDefault' value of schema and 'form' value of element are different |
I received a WSDL file which basically contains something like the following type definition:
<wsdl:types>
<xsd:schema elementFormDefault="unqualified" targetNamespace="http://example.com/wsdl/formtest/">
<xsd:element name="request">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="x" minOccurs="0" type="xsd:float" form="qualified" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
</xsd:schema>
</wsdl:types>
Note that 'elementFormDefault' and 'form' have different values. It seems like the client side of XML-Compile-SOAP does the right thing in this case, i.e. it sends a '<tns:x>1</tns:x>' to the server. The server side, however, rejects this with a faultstring of 'operation request for SOAP11 called with invalid data' and a detail/error of 'element `{http://example.com/wsdl/formtest/}x' not processed for {http://example.com/wsdl/formtest/}request at /SOAP-ENV:Envelope/SOAP-ENV:Body/tns:request/tns:x'. It works fine if 'elementFormDefault' and 'form' are both 'unqualified' or 'qualified', but not if they are different. I'm attaching a test which includes WSDL data that reproduces this behavior (it uses XML::Compile::SOAP::Daemon::PSGI to run the server side). In my understanding of the XML Schema specification, the local 'form' should override the global 'elementFormDefault', so the client seems to do the right thing and the server should process the parameter.
I'm currently using XML-Compile 1.34, XML-Compile-SOAP-Daemon 3.06 and XML-Compile-Cache 0.994 on Perl v5.14.2 from Debian Wheezy.
Subject: | form_overrides_elementFormDefault.t |
use strict;
use warnings;
use Test::More tests => 16;
use XML::Compile::WSDL11;
use XML::Compile::SOAP11;
use XML::Compile::Transport::SOAPHTTP;
use XML::Compile::SOAP::Daemon::PSGI;
use Plack::Test;
my $wsdl_template = <<EOWSDL;
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="FormTest" xmlns:tns="http://example.com/wsdl/formtest/" targetNamespace="http://example.com/wsdl/formtest/">
<wsdl:types>
<xsd:schema elementFormDefault="__FORM01__" targetNamespace="http://example.com/wsdl/formtest/">
<xsd:element name="request">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="x" minOccurs="0" type="xsd:float" form="__FORM02__" />
<xsd:element name="y" minOccurs="0" type="xsd:float" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="response">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="x" type="xsd:float" />
<xsd:element name="y" type="xsd:float" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
<wsdl:message name="request">
<wsdl:part element="tns:request" name="parameters" />
</wsdl:message>
<wsdl:message name="response">
<wsdl:part element="tns:response" name="parameters" />
</wsdl:message>
<wsdl:portType name="example">
<wsdl:operation name="request">
<wsdl:input message="tns:request" name="request" />
<wsdl:output message="tns:response" name="response" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="exampleSoapBinding" type="tns:example">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="request">
<soap:operation soapAction="request" style="document" />
<wsdl:input name="request">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="response">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="service">
<wsdl:port binding="tns:exampleSoapBinding" name="defaultPort">
<soap:address location="http://example.com/service" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
EOWSDL
my @combinations = (
[ 'qualified', 'qualified' ],
[ 'unqualified', 'qualified' ],
[ 'qualified', 'unqualified' ],
[ 'unqualified', 'unqualified' ]
);
for my $combination (@combinations) {
my $wsdl_data = $wsdl_template;
$wsdl_data =~ s{__FORM01__}{$combination->[0]};
$wsdl_data =~ s{__FORM02__}{$combination->[1]};
my $combination_str = 'elementFormDefault="'.$combination->[0].'", '
.'form="'.$combination->[1].'"';
my $wsdl = XML::Compile::WSDL11->new($wsdl_data);
my $daemon = XML::Compile::SOAP::Daemon::PSGI->new();
$daemon->operationsFromWSDL($wsdl, callbacks => {
request => sub {
my ($soap, $data) = @_;
my $x = $data->{parameters}->{x} || 0;
my $y = $data->{parameters}->{y} || 0;
return +{
x => $x,
y => $y,
};
},
});
my $client = $wsdl->compileClient('request',
server => 'localhost:4337',
transport_hook => sub {
my ($request, $trace) = @_;
my $response;
test_psgi($daemon->to_app(), sub {
my ($cb) = @_;
$response = $cb->($request);
});
return $response;
},
);
my ($answer, $trace) = $client->();
is_deeply($answer->{parameters}, { x => 0, y => 0 }, 'default x and y values, '.$combination_str)
or trace($trace);
($answer, $trace) = $client->(
x => 1,
);
is_deeply($answer->{parameters}, { x => 1, y => 0 }, 'passed x value, default y value, '.$combination_str)
or trace($trace);
($answer, $trace) = $client->(
y => 1,
);
is_deeply($answer->{parameters}, { x => 0, y => 1 }, 'default x value, passed y value, '.$combination_str)
or trace($trace);
($answer, $trace) = $client->(
x => 1,
y => 1,
);
is_deeply($answer->{parameters}, { x => 1, y => 1 }, 'passed x and y values, '.$combination_str)
or trace($trace);
}
sub trace {
my ($trace) = @_;
$trace->printRequest;
$trace->printResponse;
}