Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support namespace validation in SOAP mock APIs generated from a WSDL #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
target
*.iml
.idea
.classpath
.project
.settings
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ Endpoints are configured in `ws-mock.properties` file under `resources` folder.
<td>SERVICE[i].WSDL</td>
<td>name of wsdl file, relevant only to SOAP webservices; the wsdl file should be placed in the classpath visible to the mock service application</td>
</tr>
<tr>
<td>SERVICE[i].VALIDATE_SCHEMA</td>
<td>boolean value <b>true</b> to validate, or <b>false</b> (or not present at all) to not validate namespaces of request as defined in WSDL file</td>
</tr>
<tr>
<td>SERVICE[i].OPERATION[j].INPUT_MESSAGE</td>
<td>(optional) root element of request message; relevant only to SOAP webservices; skip this if WSDL is provided, this information will be read from the WSDL file</td>
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/fi/mystes/http/api/PATCH.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fi.mystes.http.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.ws.rs.HttpMethod;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH {

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import net.sf.jaceko.mock.service.RequestExecutor;
import org.apache.log4j.Logger;

import fi.mystes.http.api.PATCH;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
Expand Down Expand Up @@ -113,6 +115,23 @@ public Response performPutRequest(@PathParam("serviceName") String serviceName,
LOG.debug("serviceName: " + serviceName + ", response:" + mockResponse);
return buildWebserviceResponse(mockResponse);
}

@PATCH
@Consumes({"text/*", "application/*"})
public Response performPatchRequest(@PathParam("serviceName") String serviceName, @Context HttpHeaders headers, String request) {
return performPatchRequest(serviceName, null, headers, request);
}

@PATCH
@Path("/{postfix: (([^/]+?(/)?)+?)}")
@Consumes({"text/*", "application/*"})
public Response performPatchRequest(@PathParam("serviceName") String serviceName, @PathParam("postfix") String resourcePath,
@Context HttpHeaders headers, String request) {
validateResourcePath(serviceName, resourcePath);
MockResponse mockResponse = svcLayer.performRequest(serviceName, "PATCH", request, null, resourcePath, headers.getRequestHeaders());
LOG.debug("serviceName: " + serviceName + ", response:" + mockResponse);
return buildWebserviceResponse(mockResponse);
}

@DELETE
public Response performDeleteRequest(@PathParam("serviceName") String serviceName, @Context HttpHeaders headers) {
Expand Down
33 changes: 29 additions & 4 deletions src/main/java/net/sf/jaceko/mock/service/PropertyProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public class PropertyProcessor {
private static final String BINARY = "BINARY";

private static final String NAMESPACE = "NAMESPACE";

private static final String VALIDATE_SCHEMA = "VALIDATE_SCHEMA";

private static final Pattern SERVICE_PATTERN = Pattern.compile("^SERVICE\\[([0-9]+)\\]$");
private static final Pattern OPERATION_PATTERN = Pattern.compile("^OPERATION\\[([0-9]+)\\]$");
Expand Down Expand Up @@ -158,9 +160,30 @@ private void setServiceProperties(final WebService service, final String service
service.setIgnoreXmlDeclaration(Boolean.valueOf(propertyValue));
} else if (serviceProperty.equals(ENABLE_RESOURCE_PATHS)) {
service.setEnableResourcePaths(Boolean.valueOf(propertyValue));
} else if (serviceProperty.equals(VALIDATE_SCHEMA) && propertyValue.equals("true")) {
StringBuilder operationsNamespaces = getCommaSeparatedWsdlNamespaces();

Collection<WebserviceOperation> operations = service.getOperations();
for(WebserviceOperation operation : operations) {
operation.setNameSpaces(operationsNamespaces.toString());
}
}
}

private StringBuilder getCommaSeparatedWsdlNamespaces() {
StringBuilder operationsNamespaces = new StringBuilder();
Map<String, String> namespaces = wsdlProcessor.getWsdlNamespaces();
boolean first = true;
for (String ns : namespaces.values()) {
if (!first) {
operationsNamespaces.append(",");
}
operationsNamespaces.append(ns);
first = false;
}
return operationsNamespaces;
}

private WebService getService(final Map<Integer, WebService> services, final int serviceIndex) {
WebService service = services.get(serviceIndex);
if (service == null) {
Expand Down Expand Up @@ -207,10 +230,12 @@ private void setDefaultResponseText(final WebserviceOperation operation) {
if( operation.isBinary() ) {
operation.setDefaultResponseBinaryContent(fileReader.readBinaryFileContents(operation.getDefaultResponseFile()));
} else {
final String fileText = fileReader.readFileContents(operation.getDefaultResponseFile());
if (fileText != null) {
operation.setDefaultResponseText(fileText);
}
if (operation.getDefaultResponseFile() != null) {
final String fileText = fileReader.readFileContents(operation.getDefaultResponseFile());
if (fileText != null) {
operation.setDefaultResponseText(fileText);
}
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/sf/jaceko/mock/service/WsdlProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class WsdlProcessor {
private static final Logger LOG = Logger.getLogger(WsdlProcessor.class);

private WSDLFactory factory;
private Map<String, String> namespaces;

public WsdlProcessor() {
try {
Expand All @@ -59,6 +60,7 @@ public List<WebserviceOperation> getOperationsFromWsdl(final String wsdlFileName
WSDLReader wsdlReader = factory.newWSDLReader();
try {
Definition def = wsdlReader.readWSDL(null, new InputSource(new StringReader(fileText)));
namespaces = def.getNamespaces();
SoapMessageBuilder soapMessageBuilder = new SoapMessageBuilder(def, fileText);
Map<QName, Binding> bindingsMap = def.getBindings();
Collection<Binding> bindings = bindingsMap.values();
Expand Down Expand Up @@ -101,5 +103,15 @@ public List<WebserviceOperation> getOperationsFromWsdl(final String wsdlFileName

return mockOperations;
}

/**
* Gets namespaces parsed from given WSDL in getOperationsFromWsdl method.
* getOperationsFromWsdl must first be invoked.
*
* @return Map containing prefixes as keys and namespaces as values
*/
public Map<String, String> getWsdlNamespaces() {
return namespaces;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class WsdlProcessingIntegrationTest {
private static final String SERVICES = "http://localhost:8080/mock/services";

private static final String HELLO_MOCK_ENDPOINT = "http://localhost:8080/mock/services/SOAP/hello-soap-withwsdl/endpoint";

private static final String HELLO_MOCK_ENDPOINT_SCHEMA_CHECK = "http://localhost:8080/mock/services/SOAP/hello-soap-withwsdl-and-schema-check/endpoint";


private static final String HELLO_REQUEST = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:examples:helloservice\">\r\n"
+ " <soapenv:Header/>\r\n"
Expand All @@ -41,6 +44,14 @@ public class WsdlProcessingIntegrationTest {
+ " <firstName xsi:type=\"xsd:string\">{0}</firstName>\r\n"
+ " </urn:sayHello>\r\n"
+ " </soapenv:Body>\r\n" + "</soapenv:Envelope>";

private static final String HELLO_REQUEST_INVALID_NS = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:examples-invalid:helloservice\">\r\n"
+ " <soapenv:Header/>\r\n"
+ " <soapenv:Body>\r\n"
+ " <urn:sayHello soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <firstName xsi:type=\"xsd:string\">{0}</firstName>\r\n"
+ " </urn:sayHello>\r\n"
+ " </soapenv:Body>\r\n" + "</soapenv:Envelope>";

private static final String CONVERSION_RATE_ENDPOINT = "http://localhost:8080/mock/services/SOAP/webservicex-rate-convertor/endpoint";
private static final String CONVERSION_RATE_REQUEST = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://www.webserviceX.NET/\">\r\n"
Expand Down Expand Up @@ -104,5 +115,14 @@ public void shouldReturnDefaultHelloResponseGeneratedFromWsdlFile() throws Clien
Document serviceResponseDoc = new DocumentImpl(response.getBody());
assertThat(serviceResponseDoc, hasXPath("//Envelope/Body/sayHelloResponse/greeting"));
}

@Test
public void shouldResponseWithUndefinedWebserviceOperationDueToInvalidNamespaceUsingWsdlFile() throws ClientProtocolException, IOException,
ParserConfigurationException, SAXException {

MockResponse response = requestSender.sendPostRequest(HELLO_MOCK_ENDPOINT_SCHEMA_CHECK, HELLO_REQUEST_INVALID_NS, MediaType.TEXT_XML);
assertThat(response.getCode(), is(HttpStatus.SC_NOT_FOUND));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,42 @@ public void shouldReturnPUT_CONFLICTResponse() {
assertThat((String) response.getEntity(), is(responseReturnedByServiceLayer));

}

@Test
public void shouldPerformPatchRequest() {
String request = "<dummyRequest>def</dummyRequest>";
resource.performPatchRequest(SERVICE_NAME, mockHttpHeaders, request);
verify(requestExecutor).performRequest(SERVICE_NAME, "PATCH", request, null, null, headers);
}

@Test
public void shouldPerformPatchRequestWithHeaders() {
String request = "<dummyRequest>def</dummyRequest>";

headers.putSingle("someheader", "headervalue");
headers.putSingle("someotherheader", "anotherheadervalue");

resource.performPatchRequest(SERVICE_NAME, mockHttpHeaders, request);

verify(requestExecutor).performRequest(SERVICE_NAME, "PATCH", request, null, null, headers);
}

@Test
public void shouldPerformPatchRequestPassingResourceId() {
String resourceId = "resId12";
String request = "<dummyRequest>abc</dummyRequest>";
resource.performPatchRequest(SERVICE_NAME, resourceId, mockHttpHeaders, request);
verify(requestExecutor).performRequest(SERVICE_NAME, "PATCH", request, null, resourceId, headers);
}

@Test
public void shouldReturnPATCH_CONFLICTResponse() {
String responseReturnedByServiceLayer = "someResponse123";
when(requestExecutor.performRequest(anyString(), anyString(), anyString(), anyString(), anyString(), any(MultivaluedMap.class))).thenReturn(
new MockResponse(responseReturnedByServiceLayer, 409));
Response response = resource.performPatchRequest(SERVICE_NAME, mockHttpHeaders, null);
assertThat((String) response.getEntity(), is(responseReturnedByServiceLayer));
}

@Test
public void shouldPerformDeleteRequest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import net.sf.jaceko.mock.model.webservice.WebService;
import net.sf.jaceko.mock.model.webservice.WebserviceOperation;
import net.sf.jaceko.mock.util.FileReader;

import org.hamcrest.core.IsNull;
import org.junit.Test;
import org.mockito.internal.matchers.*;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

Expand All @@ -22,7 +25,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.xml.HasXPath.hasXPath;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
Expand All @@ -38,7 +41,8 @@ public PropertyProcessorFileReadingTest() {
@Test
public void shouldReadWsdlContentsFromFile() throws IOException, ParserConfigurationException, SAXException {
String propertyString = "SERVICE[0].NAME=ticketing\r\n" + "SERVICE[0].WSDL=hello-for-unit-tests.wsdl\r\n"
+ "SERVICE[0].TYPE=SOAP\r\n" + "SERVICE[0].OPERATION[0].INPUT_MESSAGE=someRequest\r\n";
+ "SERVICE[0].TYPE=SOAP\r\n" + "SERVICE[0].OPERATION[0].INPUT_MESSAGE=someRequest\r\n"
+ "SERVICE[0].VALIDATE_SCHEMA=true";

Reader reader = new StringReader(propertyString);
MockConfigurationHolder configuration = propertyProcessor.process(reader);
Expand All @@ -47,7 +51,7 @@ public void shouldReadWsdlContentsFromFile() throws IOException, ParserConfigura
String wsdlText = soapService.getWsdlText();
Document wsdlDoc = new DocumentImpl(wsdlText);
assertThat(wsdlDoc, hasXPath("/definitions/message/@name", equalTo("SayHelloRequest")));

assertThat(soapService.getOperations().iterator().next().getNameSpaces(), is(notNullValue()));
}

@Test
Expand All @@ -67,7 +71,6 @@ public void shouldReadDefaultResponseContentsFromFile() throws IOException, Pars

assertThat(responseDoc, hasXPath("/dummyResponse/reqId", equalTo("789789")));
assertThat(responseDoc, hasXPath("/dummyResponse/status", equalTo("OK")));

}

@Test
Expand All @@ -82,6 +85,7 @@ public void shouldContinueIfResponseFileNotFound() throws IOException, ParserCon
WebService soapService = services.iterator().next();
WebserviceOperation operation = soapService.getOperation(0);
assertThat(operation, notNullValue());
assertThat(operation.getNameSpaces(), nullValue());

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import net.sf.jaceko.mock.matcher.OperationHavingNameEqualTo;
import net.sf.jaceko.mock.model.webservice.WebserviceOperation;
import net.sf.jaceko.mock.util.FileReader;

import org.hamcrest.core.IsNot;
import org.hamcrest.core.IsNull;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
Expand Down Expand Up @@ -32,7 +35,8 @@ public void shouldFetchOperationsFromWsdlFile_DocumetStyle() {
assertThat(operationsFromWsdl, hasItem(new OperationHavingNameEqualTo("lockRequestElement")));
assertThat(operationsFromWsdl, hasItem(new OperationHavingNameEqualTo("unlockRequestElement")));
assertThat(operationsFromWsdl, hasItem(new OperationHavingNameEqualTo("purgeRequestElement")));

assertThat(wsdlProcessor.getWsdlNamespaces().size(), is(4));

}

@Test
Expand All @@ -41,7 +45,6 @@ public void shouldFetchOperationsFromWsdlFile_RPCStyle() {
fileReader.readFileContents("hello-for-unit-tests.wsdl"));
assertThat(operationsFromWsdl.size(), is(1));
assertThat(operationsFromWsdl, hasItem(new OperationHavingNameEqualTo("sayHello")));

}

@Test
Expand Down
49 changes: 49 additions & 0 deletions src/test/resources/it/hello-and-schema-check.wsdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<definitions name="HelloService"
targetNamespace="http://www.examples.com/wsdl/HelloServiceSchemaCheck.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.examples.com/wsdl/HelloServiceSchemaCheck.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<message name="SayHelloRequest">
<part name="firstName" type="xsd:string"/>
</message>
<message name="SayHelloResponse">
<part name="greeting" type="xsd:string"/>
</message>

<portType name="Hello_PortType">
<operation name="sayHello">
<input message="tns:SayHelloRequest"/>
<output message="tns:SayHelloResponse"/>
</operation>
</portType>

<binding name="Hello_Binding" type="tns:Hello_PortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="sayHello">
<soap:operation soapAction="sayHello"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</output>
</operation>
</binding>

<service name="Hello_Service">
<documentation>Service that says hello</documentation>
<port binding="tns:Hello_Binding" name="Hello_Port">
<soap:address
location="http://localhost:8080/mock/services/SOAP/dummy-soap-withwsdl-and-schema-check/endpoint"/>
</port>
</service>
</definitions>
5 changes: 5 additions & 0 deletions src/test/resources/it/ws-mock.properties
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,8 @@ SERVICE[9].OPERATION[0].BINARY=true
SERVICE[9].OPERATION[0].INPUT_MESSAGE=dummySoapRequest
SERVICE[9].OPERATION[0].DEFAULT_RESPONSE=default_soap_multipart_response.dat
SERVICE[9].OPERATION[0].DEFAULT_RESPONSE_CONTENT_TYPE=multipart/related;type="application/xop+xml";start="http://tempuri.org/0";boundary="boundary123123";start-info="application/soap+xml"

SERVICE[10].NAME=hello-soap-withwsdl-and-schema-check
SERVICE[10].WSDL=hello-schema-check.wsdl
SERVICE[10].TYPE=SOAP
SERVICE[10].VALIDATE_SCHEMA=true