Skip to content

Commit 895587d

Browse files
committed
fix(soap): applies message part filter from binding operation.
1 parent 15499d3 commit 895587d

File tree

7 files changed

+305
-11
lines changed

7 files changed

+305
-11
lines changed

mock/soap/src/main/java/io/gatehill/imposter/plugin/soap/parser/Wsdl1Parser.kt

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,6 @@ class Wsdl1Parser(
178178
name = operationName
179179
) ?: throw IllegalStateException("No portType operation found for portType: $portTypeName and operation: $operationName")
180180

181-
val input = getMessage(portTypeOperation, "./wsdl:input")
182-
?: throw IllegalStateException("No input found for portType operation: $operationName")
183-
184-
val output = getMessage(portTypeOperation, "./wsdl:output")
185-
?: throw IllegalStateException("No output found for portType operation: $operationName")
186-
187181
val style = soapOperation.getAttributeValue("style") ?: run {
188182
// fall back to soap:binding
189183
val soapBinding = selectSingleNode(binding, "./soap:binding")
@@ -192,6 +186,9 @@ class Wsdl1Parser(
192186
soapBinding?.getAttributeValue("style")
193187
}
194188

189+
val input = getInputOrOutput(bindingOperation, portTypeOperation, operationName, "input")
190+
val output = getInputOrOutput(bindingOperation, portTypeOperation, operationName, "output")
191+
195192
return WsdlOperation(
196193
name = bindingOperation.getAttributeValue("name"),
197194
soapAction = soapOperation.getAttributeValue("soapAction"),
@@ -201,6 +198,29 @@ class Wsdl1Parser(
201198
)
202199
}
203200

201+
private fun getInputOrOutput(
202+
bindingOperation: Element,
203+
portTypeOperation: Element,
204+
operationName: String,
205+
messageType: String,
206+
): OperationMessage {
207+
// fallback to SOAP 1.2
208+
val partFilter: List<String>? = getMessagePartFilter(bindingOperation, "./wsdl:$messageType/soap:body")
209+
?: getMessagePartFilter(bindingOperation, "./wsdl:$messageType/soap12:body")
210+
211+
val input = getMessage(portTypeOperation, "./wsdl:$messageType", partFilter)
212+
?: throw IllegalStateException("No $messageType found for portType operation: $operationName")
213+
214+
return input
215+
}
216+
217+
private fun getMessagePartFilter(bindingOperation: Element, expression: String): List<String>? {
218+
// part filter is space delimited
219+
return selectSingleNode(bindingOperation, expression)
220+
?.getAttribute("parts")?.value
221+
?.trim()?.split(' ')?.takeIf { it.isNotEmpty() }
222+
}
223+
204224
private fun getBindingElement(bindingName: String) = selectSingleNodeWithName(
205225
context = document,
206226
expressionTemplate = "/wsdl:definitions/wsdl:binding[@name='%s']",
@@ -219,7 +239,7 @@ class Wsdl1Parser(
219239
* Extract the WSDL message part element attribute, then attempt
220240
* to resolve it from within the XSD.
221241
*/
222-
private fun getMessage(context: Element, expression: String): OperationMessage? {
242+
private fun getMessage(context: Element, expression: String, partFilter: List<String>?): OperationMessage? {
223243
val inputOrOutputNode = selectSingleNode(context, expression)
224244
?: throw IllegalStateException("No input or output found for: $expression")
225245

@@ -232,8 +252,14 @@ class Wsdl1Parser(
232252
val message = selectSingleNode(document, "/wsdl:definitions/wsdl:message[@name='$messageName']")
233253
?: throw IllegalStateException("Message $msgAttr not found")
234254

235-
// look up message parts
236-
val messageParts = selectNodes(message, "./wsdl:part")
255+
// look up message parts and filter if required
256+
var messageParts = selectNodes(message, "./wsdl:part")
257+
partFilter?.let {
258+
messageParts = messageParts.filter { part ->
259+
val partName = part.getAttributeValue("name")
260+
partFilter.contains(partName)
261+
}
262+
}
237263

238264
val parts: List<OperationMessage> = messageParts.map { messagePart ->
239265
val partName = messagePart.getAttributeValue("name")

mock/soap/src/test/java/io/gatehill/imposter/plugin/soap/Wsdl1Soap11DocumentEndToEndTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ package io.gatehill.imposter.plugin.soap
4545
import io.gatehill.imposter.plugin.soap.util.SoapUtil
4646

4747
/**
48-
* Tests for [SoapPluginImpl] using WSDL v1 and SOAP 1.1.
48+
* Tests for [SoapPluginImpl] using WSDL v1 and SOAP 1.1,
49+
* using Document style.
4950
*
5051
* @author Pete Cornish
5152
*/
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2016-2021.
3+
*
4+
* This file is part of Imposter.
5+
*
6+
* "Commons Clause" License Condition v1.0
7+
*
8+
* The Software is provided to you by the Licensor under the License, as
9+
* defined below, subject to the following condition.
10+
*
11+
* Without limiting other conditions in the License, the grant of rights
12+
* under the License will not include, and the License does not grant to
13+
* you, the right to Sell the Software.
14+
*
15+
* For purposes of the foregoing, "Sell" means practicing any or all of
16+
* the rights granted to you under the License to provide to third parties,
17+
* for a fee or other consideration (including without limitation fees for
18+
* hosting or consulting/support services related to the Software), a
19+
* product or service whose value derives, entirely or substantially, from
20+
* the functionality of the Software. Any license notice or attribution
21+
* required by the License must also include this Commons Clause License
22+
* Condition notice.
23+
*
24+
* Software: Imposter
25+
*
26+
* License: GNU Lesser General Public License version 3
27+
*
28+
* Licensor: Peter Cornish
29+
*
30+
* Imposter is free software: you can redistribute it and/or modify
31+
* it under the terms of the GNU Lesser General Public License as published by
32+
* the Free Software Foundation, either version 3 of the License, or
33+
* (at your option) any later version.
34+
*
35+
* Imposter is distributed in the hope that it will be useful,
36+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
37+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38+
* GNU Lesser General Public License for more details.
39+
*
40+
* You should have received a copy of the GNU Lesser General Public License
41+
* along with Imposter. If not, see <https://www.gnu.org/licenses/>.
42+
*/
43+
package io.gatehill.imposter.plugin.soap
44+
45+
import io.gatehill.imposter.plugin.soap.util.SoapUtil
46+
47+
/**
48+
* Tests for [SoapPluginImpl] using WSDL v1 and SOAP 1.1,
49+
* with a message part filter in the endpoint binding.
50+
*
51+
* @author Pete Cornish
52+
*/
53+
class Wsdl1Soap11FilterMessagePartsEndToEndTest : AbstractEndToEndTest() {
54+
override val testConfigDirs = listOf("/wsdl1-soap11-filter-message-parts")
55+
override val soapEnvNamespace = SoapUtil.soap11EnvNamespace
56+
override val soapContentType = SoapUtil.soap11ContentType
57+
}

mock/soap/src/test/java/io/gatehill/imposter/plugin/soap/Wsdl1Soap11RpcEndToEndTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ package io.gatehill.imposter.plugin.soap
4545
import io.gatehill.imposter.plugin.soap.util.SoapUtil
4646

4747
/**
48-
* Tests for [SoapPluginImpl] using WSDL v1 and SOAP 1.1.
48+
* Tests for [SoapPluginImpl] using WSDL v1 and SOAP 1.1
49+
* using RPC style.
4950
*
5051
* @author Pete Cornish
5152
*/
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
3+
<env:Header/>
4+
<env:Body>
5+
<getPetByNameResponse xmlns="urn:com:example:petstore">
6+
<id>3</id>
7+
<name>Fluffy</name>
8+
</getPetByNameResponse>
9+
</env:Body>
10+
</env:Envelope>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
plugin: soap
2+
wsdlFile: service.wsdl
3+
4+
resources:
5+
- binding: SoapBinding
6+
operation: getPetByName
7+
response:
8+
file: getPetByNameResponse.xml
9+
10+
- binding: HttpBinding
11+
operation: getPetByName
12+
response:
13+
file: getPetByNameResponse.xml
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
~ Copyright (c) 2023.
5+
~
6+
~ This file is part of Imposter.
7+
~
8+
~ "Commons Clause" License Condition v1.0
9+
~
10+
~ The Software is provided to you by the Licensor under the License, as
11+
~ defined below, subject to the following condition.
12+
~
13+
~ Without limiting other conditions in the License, the grant of rights
14+
~ under the License will not include, and the License does not grant to
15+
~ you, the right to Sell the Software.
16+
~
17+
~ For purposes of the foregoing, "Sell" means practicing any or all of
18+
~ the rights granted to you under the License to provide to third parties,
19+
~ for a fee or other consideration (including without limitation fees for
20+
~ hosting or consulting/support services related to the Software), a
21+
~ product or service whose value derives, entirely or substantially, from
22+
~ the functionality of the Software. Any license notice or attribution
23+
~ required by the License must also include this Commons Clause License
24+
~ Condition notice.
25+
~
26+
~ Software: Imposter
27+
~
28+
~ License: GNU Lesser General Public License version 3
29+
~
30+
~ Licensor: Peter Cornish
31+
~
32+
~ Imposter is free software: you can redistribute it and/or modify
33+
~ it under the terms of the GNU Lesser General Public License as published by
34+
~ the Free Software Foundation, either version 3 of the License, or
35+
~ (at your option) any later version.
36+
~
37+
~ Imposter is distributed in the hope that it will be useful,
38+
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
39+
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40+
~ GNU Lesser General Public License for more details.
41+
~
42+
~ You should have received a copy of the GNU Lesser General Public License
43+
~ along with Imposter. If not, see <https://www.gnu.org/licenses/>.
44+
-->
45+
46+
<definitions name="PetService" xmlns="http://schemas.xmlsoap.org/wsdl/"
47+
xmlns:tns="urn:com:example:petstore"
48+
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
49+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
50+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
51+
targetNamespace="urn:com:example:petstore">
52+
53+
<documentation>
54+
This is a sample WSDL 1.1 document describing the pet service.
55+
It has SOAP 1.1 bindings.
56+
</documentation>
57+
58+
<!-- Abstract type -->
59+
<types>
60+
<xs:schema targetNamespace="urn:com:example:petstore"
61+
xmlns:tns="urn:com:example:petstore">
62+
63+
<xs:complexType name="petType">
64+
<xs:all>
65+
<xs:element name="id" type="xs:int"/>
66+
<xs:element name="name" type="xs:string"/>
67+
</xs:all>
68+
</xs:complexType>
69+
70+
<xs:complexType name="getPetByIdRequest">
71+
<xs:all>
72+
<xs:element name="id" type="xs:int"/>
73+
</xs:all>
74+
</xs:complexType>
75+
76+
<xs:complexType name="getPetByNameRequest">
77+
<xs:all>
78+
<xs:element name="name" type="xs:string"/>
79+
</xs:all>
80+
</xs:complexType>
81+
82+
<xs:element name="getPetByIdRequest" type="tns:getPetByIdRequest"/>
83+
<xs:element name="getPetByIdResponse" type="tns:petType"/>
84+
85+
<xs:element name="getPetByNameRequest" type="tns:getPetByNameRequest"/>
86+
<xs:element name="getPetByNameResponse" type="tns:petType"/>
87+
88+
<xs:element name="fault" type="xs:string"/>
89+
</xs:schema>
90+
</types>
91+
92+
<message name="getPetByIdRequest">
93+
<part type="xs:string" name="header"/>
94+
<part element="tns:getPetByIdRequest" name="body"/>
95+
</message>
96+
<message name="getPetByIdResponse">
97+
<part element="tns:getPetByIdResponse" name="body"/>
98+
</message>
99+
<message name="getPetByNameRequest">
100+
<part type="xs:string" name="header"/>
101+
<part element="tns:getPetByNameRequest" name="body"/>
102+
</message>
103+
<message name="getPetByNameResponse">
104+
<part element="tns:getPetByNameResponse" name="body"/>
105+
</message>
106+
107+
<!-- Abstract port types -->
108+
<portType name="PetPortType">
109+
<operation name="getPetById">
110+
<input message="tns:getPetByIdRequest" name="getPetByIdRequest"/>
111+
<output message="tns:getPetByIdResponse" name="getPetByIdResponse"/>
112+
</operation>
113+
<operation name="getPetByName">
114+
<input message="tns:getPetByNameRequest" name="getPetByNameRequest"/>
115+
<output message="tns:getPetByNameResponse" name="getPetByNameResponse"/>
116+
</operation>
117+
</portType>
118+
119+
<!-- Concrete Binding Over HTTP -->
120+
<binding name="HttpBinding" type="tns:PetPortType">
121+
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
122+
123+
<operation name="getPetById">
124+
<soap:operation soapAction="getPetById" style="document"/>
125+
<input name="getPetByIdRequest">
126+
<!-- specify the part(s) to use from the message -->
127+
<soap:header message="getPetByIdRequest" part="header" use="literal"/>
128+
<soap:body parts="body" use="literal"/>
129+
</input>
130+
<output name="getPetByIdResponse">
131+
<soap:body use="literal"/>
132+
</output>
133+
</operation>
134+
<operation name="getPetByName">
135+
<soap:operation soapAction="getPetByName" style="document"/>
136+
<input name="getPetByNameRequest">
137+
<!-- specify the part(s) to use from the message -->
138+
<soap:header message="getPetByNameRequest" part="header" use="literal"/>
139+
<soap:body parts="body" use="literal"/>
140+
</input>
141+
<output name="getPetByNameResponse">
142+
<soap:body use="literal"/>
143+
</output>
144+
</operation>
145+
</binding>
146+
147+
<!-- Concrete Binding with SOAP-->
148+
<binding name="SoapBinding" type="tns:PetPortType">
149+
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/soap"/>
150+
151+
<operation name="getPetById">
152+
<soap:operation soapAction="getPetById" style="document"/>
153+
<input name="getPetByIdRequest">
154+
<!-- specify the part(s) to use from the message -->
155+
<soap:header message="getPetByIdRequest" part="header" use="literal"/>
156+
<soap:body parts="body" use="literal"/>
157+
</input>
158+
<output name="getPetByIdResponse">
159+
<soap:body use="literal"/>
160+
</output>
161+
</operation>
162+
163+
<operation name="getPetByName">
164+
<!-- soap:operation style omitted - fall back to soap:binding style -->
165+
<soap:operation soapAction="getPetByName"/>
166+
<input name="getPetByNameRequest">
167+
<!-- specify the part(s) to use from the message -->
168+
<soap:header message="getPetByNameRequest" part="header" use="literal"/>
169+
<soap:body parts="body" use="literal"/>
170+
</input>
171+
<output name="getPetByNameResponse">
172+
<soap:body use="literal"/>
173+
</output>
174+
</operation>
175+
</binding>
176+
177+
<!-- Web Service offering endpoints for both bindings-->
178+
<service name="PetService">
179+
<port name="HttpEndpoint" binding="tns:HttpBinding">
180+
<soap:address location="http://www.example.com/http/"/>
181+
</port>
182+
<port name="SoapEndpoint" binding="tns:SoapBinding">
183+
<soap:address location="http://www.example.com/soap/"/>
184+
</port>
185+
</service>
186+
</definitions>

0 commit comments

Comments
 (0)