├── .github ├── CODEOWNERS ├── workflows │ ├── pull_request_ubuntu_build.yml │ └── push_ubuntu_build.yml └── ISSUE_TEMPLATE │ ├── task.yml │ ├── config.yml │ ├── improvement.yml │ ├── new-feature.yml │ └── bug.yml ├── src ├── test │ ├── resources │ │ ├── complex │ │ │ ├── arrayMessage.json │ │ │ ├── nestedMessage.json │ │ │ ├── choice.xsd │ │ │ ├── groups.xsd │ │ │ ├── arrays.xsd │ │ │ ├── arrays.wsdl │ │ │ ├── choice.wsdl │ │ │ ├── groups.wsdl │ │ │ ├── invalid.wsdl │ │ │ ├── nested.wsdl │ │ │ └── nested.xsd │ │ ├── calculator │ │ │ ├── calculator.xsd │ │ │ └── calculator.wsdl │ │ ├── calculator-remote │ │ │ ├── calculator.xsd │ │ │ └── calculator.wsdl │ │ └── issue-28 │ │ │ └── failing.wsdl │ └── java │ │ └── org │ │ └── wso2 │ │ └── soaptorest │ │ ├── utils │ │ └── ListJSONPathsTest.java │ │ ├── WSDLExceptionTest.java │ │ ├── SOAPToRESTConverterTest.java │ │ └── ComplexWSDLTestCase.java └── main │ └── java │ └── org │ └── wso2 │ └── soaptorest │ ├── exceptions │ └── SOAPToRESTException.java │ ├── models │ ├── WSDLParameter.java │ ├── XSSequence.java │ ├── XSAttribute.java │ ├── XSChoice.java │ ├── SOAPRequestElement.java │ ├── XSGroup.java │ ├── XSElement.java │ ├── SOAPtoRESTConversionData.java │ ├── XSDataType.java │ ├── XSModel.java │ ├── WSDLInfo.java │ └── WSDLSOAPOperation.java │ ├── SOAPToRESTConverter.java │ ├── utils │ ├── SOAPToRESTConstants.java │ ├── ListJSONPaths.java │ ├── WSDLProcessingUtil.java │ └── SOAPOperationExtractingUtil.java │ ├── WSDLProcessor.java │ ├── OASGenerator.java │ └── SOAPRequestBodyGenerator.java ├── .gitignore ├── limitations.md ├── pull_request_template.md ├── README.md ├── pom.xml └── LICENSE /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # See: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in the repo. 7 | * @rosensilva 8 | -------------------------------------------------------------------------------- /src/test/resources/complex/arrayMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputModel": { 3 | "inner1": { 4 | "inner2": { 5 | "inner3": { 6 | "intA": 0, 7 | "intB": 0 8 | }, 9 | "int3": 0 10 | }, 11 | "int2": 0 12 | }, 13 | "int1": 0 14 | } 15 | } -------------------------------------------------------------------------------- /src/test/resources/complex/nestedMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputModel": { 3 | "inner1": { 4 | "inner2": { 5 | "inner3": { 6 | "intA": 0, 7 | "intB": 0 8 | }, 9 | "int3": 0 10 | }, 11 | "int2": 0 12 | }, 13 | "int1": 0 14 | } 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .idea/ 25 | .classpath 26 | .project 27 | .settings/ 28 | /target/ 29 | -------------------------------------------------------------------------------- /.github/workflows/pull_request_ubuntu_build.yml: -------------------------------------------------------------------------------- 1 | name: CI - Pull request - Ubuntu 2 | 3 | on: 4 | pull_request: 5 | branches: [ "main" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '11' 18 | distribution: 'temurin' 19 | cache: maven 20 | - name: Build with Maven 21 | run: mvn clean install 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.yml: -------------------------------------------------------------------------------- 1 | name: "✍️ Create a Task" 2 | description: Create a new task. 3 | labels: ["Type/Task"] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Description 9 | description: A clear description of what needs to be done. 10 | validations: 11 | required: true 12 | - type: input 13 | id: version 14 | attributes: 15 | label: Version 16 | description: Enter product/component version. 17 | validations: 18 | required: false 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: '📚 Documentation Issue' 4 | about: Request a new article, missing topic, or report an issue if a topic is incorrect in the current documentation. 5 | url: https://github.com/wso2/docs-mi/issues/new/choose 6 | - name: General Question 7 | url: https://stackoverflow.com/questions/tagged/wso2-micro-integrator 8 | about: "If you have a question then please ask on Stack Overflow using the #wso2-micro-integrator tag." 9 | - name: Chat on help-micro-integrator Discord Channel 10 | url: https://discord.com/invite/Xa5VubmThw 11 | about: "Chat about anything else with the community." 12 | -------------------------------------------------------------------------------- /.github/workflows/push_ubuntu_build.yml: -------------------------------------------------------------------------------- 1 | name: CI - Push request - Ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '11' 18 | distribution: 'temurin' 19 | cache: maven 20 | - name: Build with Maven 21 | run: mvn clean install 22 | - name: Upload test coverage to Codecov 23 | uses: codecov/codecov-action@v4.0.1 24 | with: 25 | flags: unit_tests 26 | token: ${{ secrets.CODECOV_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/improvement.yml: -------------------------------------------------------------------------------- 1 | name: "🚀 Improvement Request" 2 | description: Suggest an improvement to the product. 3 | labels: ["Type/Improvement"] 4 | body: 5 | - type: textarea 6 | id: limitation 7 | attributes: 8 | label: Current Limitation 9 | description: Describe the the current limitation. 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: suggestion 14 | attributes: 15 | label: Suggested Improvement 16 | description: Describe the the improvement you suggest. 17 | validations: 18 | required: true 19 | - type: input 20 | id: version 21 | attributes: 22 | label: Version 23 | description: Enter component version. 24 | validations: 25 | required: false 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-feature.yml: -------------------------------------------------------------------------------- 1 | name: "💡 New Feature Request" 2 | description: Suggest new functionality and features for the product. 3 | labels: ["Type/NewFeature"] 4 | body: 5 | - type: textarea 6 | id: problem 7 | attributes: 8 | label: Problem 9 | description: What is the problem this feature will solve? 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: solution 14 | attributes: 15 | label: Proposed Solution 16 | description: Describe the solution you'd like to have. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: alternatives 21 | attributes: 22 | label: Alternatives 23 | description: Describe any alternatives have you considered 24 | validations: 25 | required: false 26 | - type: input 27 | id: version 28 | attributes: 29 | label: Version 30 | description: Enter product/component version. 31 | validations: 32 | required: false 33 | -------------------------------------------------------------------------------- /src/test/resources/complex/choice.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: "🐞 Report a Bug" 2 | description: Create an issue if something does not work as expected. 3 | labels: ["Type/Bug"] 4 | body: 5 | - type: textarea 6 | id: background 7 | attributes: 8 | label: Description 9 | description: Please share a clear and concise description of the problem. 10 | placeholder: Description 11 | - type: textarea 12 | id: steps 13 | attributes: 14 | label: Steps to Reproduce 15 | description: List the steps you followed when you encountered the issue. Provide sample source code to reproduce the issue where applicable. 16 | validations: 17 | required: true 18 | - type: input 19 | id: version 20 | attributes: 21 | label: Version 22 | description: Enter product/component version. 23 | validations: 24 | required: true 25 | - type: textarea 26 | id: environment 27 | attributes: 28 | label: Environment Details (with versions) 29 | description: Mention the environment details (OS, Client, etc.) that the product is running on. 30 | validations: 31 | required: false 32 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/exceptions/SOAPToRESTException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.exceptions; 19 | 20 | /** 21 | * Custom exception class to be thrown when an issue encountered in SOAP to REST Conversion. 22 | */ 23 | public class SOAPToRESTException extends Exception { 24 | 25 | public SOAPToRESTException(String msg, Throwable e) { 26 | 27 | super(msg, e); 28 | } 29 | 30 | public SOAPToRESTException(String msg) { 31 | 32 | super(msg); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/WSDLParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import javax.xml.namespace.QName; 21 | 22 | public class WSDLParameter { 23 | 24 | private final QName qName; 25 | private final MessageType messageType; 26 | 27 | public WSDLParameter(QName qName, MessageType messageType) { 28 | 29 | this.qName = qName; 30 | this.messageType = messageType; 31 | } 32 | 33 | public QName getQName() { 34 | 35 | return qName; 36 | } 37 | 38 | public MessageType getMessageType() { 39 | 40 | return messageType; 41 | } 42 | 43 | public enum MessageType { 44 | 45 | ELEMENT, TYPE 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSSequence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Model for represent Sequence element in the given XSD 25 | */ 26 | public class XSSequence { 27 | 28 | List elementList = new ArrayList<>(); 29 | List sequenceList = new ArrayList<>(); 30 | List choiceList = new ArrayList<>(); 31 | 32 | public List getElementList() { 33 | 34 | return elementList; 35 | } 36 | 37 | public void addElement(XSElement element) { 38 | 39 | elementList.add(element); 40 | } 41 | 42 | public List getSequenceList() { 43 | 44 | return sequenceList; 45 | } 46 | 47 | public List getChoiceList() { 48 | 49 | return choiceList; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/resources/complex/groups.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSAttribute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import javax.xml.namespace.QName; 21 | 22 | /** 23 | * Model for represent Attribute in the given XSD 24 | */ 25 | public class XSAttribute { 26 | 27 | QName name; 28 | QName type; 29 | QName refKey; 30 | XSDataType inlineComplexType; 31 | 32 | public QName getName() { 33 | 34 | return name; 35 | } 36 | 37 | public void setName(QName name) { 38 | 39 | this.name = name; 40 | } 41 | 42 | public QName getType() { 43 | 44 | return type; 45 | } 46 | 47 | public void setType(QName type) { 48 | 49 | this.type = type; 50 | } 51 | 52 | public void setInlineComplexType(XSDataType inlineComplexType) { 53 | 54 | this.inlineComplexType = inlineComplexType; 55 | } 56 | 57 | public void setRefKey(QName refKey) { 58 | 59 | this.refKey = refKey; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/resources/complex/arrays.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSChoice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Model for represent Choice element in the given XSD 25 | */ 26 | public class XSChoice { 27 | 28 | List sequenceList = new ArrayList<>(); 29 | List choiceList = new ArrayList<>(); 30 | List groupsList = new ArrayList<>(); 31 | List elementList = new ArrayList<>(); 32 | 33 | public List getSequenceList() { 34 | 35 | return sequenceList; 36 | } 37 | 38 | public List getChoiceList() { 39 | 40 | return choiceList; 41 | } 42 | 43 | public List getGroupsList() { 44 | 45 | return groupsList; 46 | } 47 | 48 | public List getElementList() { 49 | 50 | return elementList; 51 | } 52 | 53 | public void addElement(XSElement element) { 54 | 55 | elementList.add(element); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/SOAPRequestElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import org.w3c.dom.Document; 21 | 22 | /** 23 | * Data model for a single soap request. 24 | */ 25 | public class SOAPRequestElement { 26 | 27 | private final Document soapRequestBody; 28 | private final String soapAction; 29 | private final String soapNamespace; 30 | private final String nameSpace; 31 | 32 | public SOAPRequestElement(Document soapRequestBody, String soapAction, String namespace, String soapNamespace) { 33 | 34 | this.soapRequestBody = soapRequestBody; 35 | this.soapAction = soapAction; 36 | this.nameSpace = namespace; 37 | this.soapNamespace = soapNamespace; 38 | } 39 | 40 | public Document getSoapRequestBody() { 41 | 42 | return soapRequestBody; 43 | } 44 | 45 | public String getSoapAction() { 46 | 47 | return soapAction; 48 | } 49 | 50 | public String getSoapNamespace() { 51 | 52 | return soapNamespace; 53 | } 54 | 55 | public String getNamespace() { 56 | 57 | return nameSpace; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import javax.xml.namespace.QName; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Model for represent Group element in the given XSD 26 | */ 27 | public class XSGroup { 28 | 29 | QName refKey; 30 | QName name; 31 | List sequenceList = new ArrayList<>(); 32 | List choiceList = new ArrayList<>(); 33 | 34 | public List getSequenceList() { 35 | 36 | return sequenceList; 37 | } 38 | 39 | public void addSequence(XSSequence sequence) { 40 | 41 | sequenceList.add(sequence); 42 | } 43 | 44 | public List getChoiceList() { 45 | 46 | return choiceList; 47 | } 48 | 49 | public QName getRefKey() { 50 | 51 | return refKey; 52 | } 53 | 54 | public void setRefKey(QName refKey) { 55 | 56 | this.refKey = refKey; 57 | } 58 | 59 | public QName getName() { 60 | return name; 61 | } 62 | 63 | public void setName(QName name) { 64 | this.name = name; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/org/wso2/soaptorest/utils/ListJSONPathsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.utils; 19 | 20 | import org.apache.commons.io.IOUtils; 21 | import org.junit.jupiter.api.Test; 22 | 23 | import java.io.FileInputStream; 24 | import java.io.IOException; 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertTrue; 30 | 31 | class ListJSONPathsTest { 32 | 33 | @Test 34 | void getJsonPaths() throws IOException { 35 | ArrayList expectedJSONPaths = new ArrayList<>(Arrays.asList("inputModel.int1", "inputModel.inner1.inner2.inner3.intB", 36 | "inputModel.inner1.inner2.inner3.intA", "inputModel.inner1.inner2.int3", "inputModel.inner1.int2")); 37 | 38 | try (FileInputStream inputStream = new FileInputStream("src/test/resources/complex/nestedMessage.json")) { 39 | String jsonString = IOUtils.toString(inputStream, StandardCharsets.UTF_8); 40 | ArrayList jsonPaths = ListJSONPaths.getJsonPaths(jsonString); 41 | assertTrue(jsonPaths.containsAll(expectedJSONPaths) && expectedJSONPaths.containsAll(jsonPaths)); 42 | } 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations of SOAP to REST Feature 2 | 3 | ## XSD Schema Support 4 | - xs:choice: ignore the ability to choose and generate with all the elements as required(need to change the generated body as required) 5 | - xs:annotations: not supported 6 | - xs:attributes: Working for basic attributes 7 | - Not supported: Default and Fixed Values for Attributes, Optional and Required Attributes 8 | - xs:restrictions: Read base type only. Restrictions won’t apply 9 | - xs:extensions: Generates with some usability issues. Can edit via IDE 10 | - xs:complexType: If extended from the base, not generating 100% correct mapping, but can edit in the IDE 11 | - xs:complexContent: works but with limitations if nested extensions present 12 | - xs:simpleContent: works but with limitations if nested extensions present 13 | - xs:all: generates the mapping but ignores the maxOccures restrictions 14 | - xs:groups: generate the mapping without errors, group elements are ignored 15 | - xs:attributeGroups: generate the mapping without errors, group elements are ignored 16 | - xs:any: ignored but generated without errors 17 | - xs:anyAttribute: ignored but generated without errors 18 | - maxOccurs: only taken care of the unbounded case for arrays 19 | - minOccurs: ignored and generate the mapping 20 | - Mixed Content: ignored but generated normal XML payload 21 | - substitutionGroup: ignored and generated 22 | - Unions: are not supported 23 | - Enumeration: not supported 24 | 25 | Primitive Data types defined in XSD Schema Specification 26 | Following data types are treated as string: 27 | - anyURI 28 | - base64Binary 29 | - Duration 30 | - hexBinary 31 | - gDay 32 | - gMonth 33 | - gMonthDay 34 | - gYear 35 | - gYearMonth 36 | - NOTATION 37 | - QName 38 | 39 | First-class support 40 | - Boolean 41 | - Date 42 | - dateTime 43 | - Decimal 44 | - Double 45 | - Float 46 | - String 47 | - Time 48 | 49 | NOTE: All the not supported or partially generated items can be edited in the generated payload from Integration Studio IDE 50 | -------------------------------------------------------------------------------- /src/test/resources/complex/arrays.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/resources/complex/choice.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/resources/complex/groups.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/resources/complex/invalid.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/resources/complex/nested.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import javax.xml.namespace.QName; 21 | 22 | /** 23 | * Model for represent Element in the given XSD 24 | */ 25 | public class XSElement { 26 | 27 | QName name; 28 | QName type; 29 | QName refKey; 30 | boolean isOptional; 31 | XSDataType inlineComplexType; 32 | boolean isArray; 33 | 34 | public QName getName() { 35 | 36 | return name; 37 | } 38 | 39 | public void setName(QName name) { 40 | 41 | this.name = name; 42 | } 43 | 44 | public QName getType() { 45 | 46 | return type; 47 | } 48 | 49 | public void setType(QName type) { 50 | 51 | this.type = type; 52 | } 53 | 54 | public XSDataType getInlineComplexType() { 55 | 56 | return inlineComplexType; 57 | } 58 | 59 | public void setInlineComplexType(XSDataType inlineComplexType) { 60 | 61 | this.inlineComplexType = inlineComplexType; 62 | } 63 | 64 | public QName getRefKey() { 65 | 66 | return refKey; 67 | } 68 | 69 | public void setRefKey(QName refKey) { 70 | 71 | this.refKey = refKey; 72 | } 73 | 74 | public boolean isArray() { 75 | return isArray; 76 | } 77 | 78 | public void setArray(boolean array) { 79 | isArray = array; 80 | } 81 | 82 | public boolean isOptional() { 83 | return isOptional; 84 | } 85 | 86 | public void setOptional(boolean optional) { 87 | isOptional = optional; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/SOAPtoRESTConversionData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import io.swagger.v3.core.util.Yaml; 21 | import io.swagger.v3.oas.models.OpenAPI; 22 | 23 | import java.util.Map; 24 | import java.util.Set; 25 | 26 | /** 27 | * Data object that represent the SOAP Endpoint as OpenAPI including the soap request message format for all the 28 | * soap endpoints. 29 | */ 30 | public class SOAPtoRESTConversionData { 31 | 32 | private final OpenAPI openAPI; 33 | private final Map soapRequestBodyMapping; 34 | private final String soapService; 35 | private final String soapPort; 36 | 37 | public SOAPtoRESTConversionData(OpenAPI openAPI, Map soapRequestBodyMapping, 38 | String soapService, String soapPort) { 39 | 40 | this.openAPI = openAPI; 41 | this.soapRequestBodyMapping = soapRequestBodyMapping; 42 | this.soapService = soapService; 43 | this.soapPort = soapPort; 44 | } 45 | 46 | public Set> getAllSOAPRequestBodies() { 47 | 48 | return soapRequestBodyMapping.entrySet(); 49 | } 50 | 51 | public String getOASString() { 52 | 53 | return Yaml.pretty(openAPI); 54 | } 55 | 56 | public String getSoapService() { 57 | 58 | return soapService; 59 | } 60 | 61 | public String getSoapPort() { 62 | 63 | return soapPort; 64 | } 65 | 66 | public OpenAPI getOpenAPI() { 67 | return openAPI; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSDataType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import javax.xml.namespace.QName; 21 | 22 | /** 23 | * Model for represent Data Type in the given XSD 24 | */ 25 | public class XSDataType { 26 | 27 | QName name; 28 | QName extensionBase; 29 | XSSequence xsSequence; 30 | XSChoice xsChoice; 31 | XSGroup xsGroup; 32 | boolean isSimpleType = false; 33 | 34 | public QName getName() { 35 | 36 | return name; 37 | } 38 | 39 | public void setName(QName name) { 40 | 41 | this.name = name; 42 | } 43 | 44 | public XSSequence getSequence() { 45 | 46 | return xsSequence; 47 | } 48 | 49 | public void setSequence(XSSequence sequence) { 50 | 51 | this.xsSequence = sequence; 52 | } 53 | 54 | public XSChoice getChoice() { 55 | 56 | return xsChoice; 57 | } 58 | 59 | public void setChoice(XSChoice choice) { 60 | 61 | this.xsChoice = choice; 62 | } 63 | 64 | public XSGroup getGroup() { 65 | 66 | return xsGroup; 67 | } 68 | 69 | public void setGroup(XSGroup group) { 70 | 71 | this.xsGroup = group; 72 | } 73 | 74 | public QName getExtensionBase() { 75 | 76 | return extensionBase; 77 | } 78 | 79 | public void setExtensionBase(QName extensionBase) { 80 | 81 | this.extensionBase = extensionBase; 82 | } 83 | 84 | public boolean isSimpleType() { 85 | return isSimpleType; 86 | } 87 | 88 | public void setSimpleType(boolean simpleType) { 89 | isSimpleType = simpleType; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/XSModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * Model for represent the all the Data Types in the given XSD 25 | */ 26 | public class XSModel { 27 | 28 | List elements = new ArrayList<>(); 29 | List attributes = new ArrayList<>(); 30 | List xsDataTypes = new ArrayList<>(); 31 | List groups = new ArrayList<>(); 32 | String targetNamespace; 33 | 34 | private boolean elementFormDefaultQualified = false; 35 | 36 | public void addElement(XSElement element) { 37 | 38 | elements.add(element); 39 | } 40 | 41 | public void addAttribute(XSAttribute attribute) { 42 | 43 | attributes.add(attribute); 44 | } 45 | 46 | public List getXsDataTypes() { 47 | 48 | return xsDataTypes; 49 | } 50 | 51 | public void addXSDataType(XSDataType complexType) { 52 | 53 | xsDataTypes.add(complexType); 54 | } 55 | 56 | public void addGroup(XSGroup group) { 57 | 58 | groups.add(group); 59 | } 60 | 61 | public List getGroups() { 62 | return groups; 63 | } 64 | 65 | public void setTargetNamespace(String targetNamespace) { 66 | 67 | this.targetNamespace = targetNamespace; 68 | } 69 | 70 | public List getElements() { 71 | return elements; 72 | } 73 | 74 | public boolean isElementFormDefaultQualified() { 75 | 76 | return elementFormDefaultQualified; 77 | } 78 | 79 | public void setElementFormDefaultQualified(boolean elementFormDefaultQualified) { 80 | 81 | this.elementFormDefaultQualified = elementFormDefaultQualified; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/resources/calculator/calculator.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/test/java/org/wso2/soaptorest/WSDLExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import org.junit.jupiter.api.Test; 21 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 22 | 23 | import java.net.MalformedURLException; 24 | import java.net.URL; 25 | 26 | import static org.junit.jupiter.api.Assertions.assertThrows; 27 | import static org.junit.jupiter.api.Assertions.assertTrue; 28 | 29 | public class WSDLExceptionTest { 30 | 31 | @Test 32 | void invalidWSDLFileLocation() { 33 | Exception exception = assertThrows(SOAPToRESTException.class, () -> SOAPToRESTConverter.getSOAPtoRESTConversionData("invalid " + 34 | "file", "test api", "1.0.0")); 35 | 36 | String expectedMessage = "Error while reading WSDL document"; 37 | String actualMessage = exception.getMessage(); 38 | 39 | assertTrue(actualMessage.contains(expectedMessage)); 40 | } 41 | 42 | @Test 43 | void invalidWSDLURLLocation() { 44 | Exception exception = assertThrows(MalformedURLException.class, () -> SOAPToRESTConverter.getSOAPtoRESTConversionData(new URL( 45 | "invalid url"), "test api", "1.0.0")); 46 | 47 | String expectedMessage = "invalid url"; 48 | String actualMessage = exception.getMessage(); 49 | 50 | assertTrue(actualMessage.contains(expectedMessage)); 51 | } 52 | 53 | @Test 54 | void invalidXSDReferredFileLocation() { 55 | Exception exception = assertThrows(SOAPToRESTException.class, () -> SOAPToRESTConverter.getSOAPtoRESTConversionData("src/" + 56 | "test/resources/complex/invalid.wsdl", "Test API", "1.0.0")); 57 | 58 | String expectedMessage = "Cannot process the provide WSDL file"; 59 | String actualMessage = exception.getMessage(); 60 | 61 | assertTrue(actualMessage.contains(expectedMessage)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/resources/calculator-remote/calculator.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/WSDLInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import java.util.Set; 21 | 22 | /** 23 | * Information extracted from WSDL. 24 | */ 25 | public class WSDLInfo { 26 | 27 | private String version; 28 | private boolean hasSoapBindingOperations; 29 | private boolean hasSoap12BindingOperations; 30 | private Set soapBindingOperations; 31 | private String soapService; 32 | private String soapPort; 33 | 34 | public String getVersion() { 35 | 36 | return version; 37 | } 38 | 39 | public void setVersion(String version) { 40 | 41 | this.version = version; 42 | } 43 | 44 | public void setHasSoapBindingOperations(boolean hasSoapBindingOperations) { 45 | 46 | this.hasSoapBindingOperations = hasSoapBindingOperations; 47 | } 48 | 49 | public boolean hasSoapBindingOperations() { 50 | 51 | return hasSoapBindingOperations; 52 | } 53 | 54 | public boolean isHasSoap12BindingOperations() { 55 | 56 | return hasSoap12BindingOperations; 57 | } 58 | 59 | public void setHasSoap12BindingOperations(boolean hasSoap12BindingOperations) { 60 | 61 | this.hasSoap12BindingOperations = hasSoap12BindingOperations; 62 | } 63 | 64 | public Set getSoapBindingOperations() { 65 | 66 | return soapBindingOperations; 67 | } 68 | 69 | public void setSoapBindingOperations(Set soapBindingOperations) { 70 | 71 | this.soapBindingOperations = soapBindingOperations; 72 | } 73 | 74 | public String getSoapService() { 75 | 76 | return this.soapService; 77 | } 78 | 79 | public void setSoapService(String soapService) { 80 | 81 | this.soapService = soapService; 82 | } 83 | 84 | public String getSoapPort() { 85 | 86 | return this.soapPort; 87 | } 88 | 89 | public void setSoapPort(String soapPort) { 90 | 91 | this.soapPort = soapPort; 92 | } 93 | } -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | > Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc. 3 | 4 | ## Goals 5 | > Describe the solutions that this feature/fix will introduce to resolve the problems described above 6 | 7 | ## Approach 8 | > Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here. 9 | 10 | ## User stories 11 | > Summary of user stories addressed by this change> 12 | 13 | ## Release note 14 | > Brief description of the new feature or bug fix as it will appear in the release notes 15 | 16 | ## Documentation 17 | > Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact 18 | 19 | ## Training 20 | > Link to the PR for changes to the training content in https://github.com/wso2/WSO2-Training, if applicable 21 | 22 | ## Certification 23 | > Type “Sent” when you have provided new/updated certification questions, plus four answers for each question (correct answer highlighted in bold), based on this change. Certification questions/answers should be sent to certification@wso2.com and NOT pasted in this PR. If there is no impact on certification exams, type “N/A” and explain why. 24 | 25 | ## Marketing 26 | > Link to drafts of marketing content that will describe and promote this feature, including product page changes, technical articles, blog posts, videos, etc., if applicable 27 | 28 | ## Automation tests 29 | - Unit tests 30 | > Code coverage information 31 | - Integration tests 32 | > Details about the test cases and coverage 33 | 34 | ## Security checks 35 | - Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes/no 36 | - Ran FindSecurityBugs plugin and verified report? yes/no 37 | - Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes/no 38 | 39 | ## Samples 40 | > Provide high-level details about the samples related to this feature 41 | 42 | ## Related PRs 43 | > List any other related PRs 44 | 45 | ## Migrations (if applicable) 46 | > Describe migration steps and platforms on which migration has been tested 47 | 48 | ## Test environment 49 | > List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested 50 | 51 | ## Learning 52 | > Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem. -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/SOAPToRESTConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import io.swagger.v3.oas.models.OpenAPI; 21 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 22 | import org.wso2.soaptorest.models.SOAPtoRESTConversionData; 23 | import org.wso2.soaptorest.models.WSDLInfo; 24 | import org.wso2.soaptorest.utils.SOAPOperationExtractingUtil; 25 | 26 | import java.net.URL; 27 | 28 | public class SOAPToRESTConverter { 29 | public static SOAPtoRESTConversionData getSOAPtoRESTConversionData(URL url, String apiTitle, String apiVersion) throws 30 | SOAPToRESTException { 31 | 32 | WSDLProcessor wsdlProcessor = new WSDLProcessor(); 33 | wsdlProcessor.init(url); 34 | SOAPOperationExtractingUtil soapOperationExtractingUtil = new SOAPOperationExtractingUtil(); 35 | WSDLInfo wsdlInfo = soapOperationExtractingUtil.getWsdlInfo(wsdlProcessor.getWsdlDefinition()); 36 | OpenAPI openAPI = OASGenerator.generateOpenAPIFromWSDL(wsdlInfo, wsdlProcessor.xsdDataModels, apiTitle, 37 | apiVersion); 38 | return SOAPRequestBodyGenerator.generateSOAPtoRESTConversionObjectFromOAS(openAPI, wsdlInfo.getSoapService(), 39 | wsdlInfo.getSoapPort()); 40 | } 41 | 42 | public static SOAPtoRESTConversionData getSOAPtoRESTConversionData(String filePath, String apiTitle, 43 | String apiVersion) throws SOAPToRESTException { 44 | 45 | WSDLProcessor wsdlProcessor = new WSDLProcessor(); 46 | wsdlProcessor.init(filePath); 47 | SOAPOperationExtractingUtil soapOperationExtractingUtil = new SOAPOperationExtractingUtil(); 48 | WSDLInfo wsdlInfo = soapOperationExtractingUtil.getWsdlInfo(wsdlProcessor.getWsdlDefinition()); 49 | OpenAPI openAPI = OASGenerator.generateOpenAPIFromWSDL(wsdlInfo, wsdlProcessor.xsdDataModels, apiTitle, 50 | apiVersion); 51 | return SOAPRequestBodyGenerator.generateSOAPtoRESTConversionObjectFromOAS(openAPI, wsdlInfo.getSoapService(), 52 | wsdlInfo.getSoapPort()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOAP to REST Conversion Java Library 2 | 3 | ## Introduction 4 | 5 | This open source library is capable of converting SOAP backend service to REST backends. The library is written to get 6 | the SOAP Webservice contract using WSDL file and generate OpenAPI Specification 7 | (Swagger file) along with the required SOAP Request bodies that will convert the REST JSON input to SOAP message. There 8 | are two methods to convert the SOAP Endpoints. Either using the URL of the real SOAP backend or the Zip file contain all 9 | the WSDL files and respective XSD files. 10 | 11 | ## How to build 12 | 13 | Clone the repository and build the project using executing following command in terminal 14 | 15 | ```mvn clean install``` 16 | 17 | Prerequisites 18 | 19 | * Maven 3.6.3 or higher (tested on 3.6.3) 20 | * Java 11.0 or higher (tested on java 11) 21 | 22 | ## How to use 23 | 24 | There are two methods in SOAPToRestConverter.java that convert the WSDL to REST Endpoint 25 | 26 | 1) Use the following method to generate REST service from WSDL URL 27 | 28 | ```java 29 | getSOAPtoRESTConversionData(URL url,String apiTitle,String apiVersion) 30 | ``` 31 | 32 | 2) Use the following method to generate REST service from WSDL File 33 | 34 | ```java 35 | getSOAPtoRESTConversionData(String filePath,String apiTitle,String apiVersion) 36 | ``` 37 | 38 | Both methods will return an object from SOAPtoRESTConversionData.java class which contains following 39 | 40 | ```java 41 | OpenAPI openAPI; 42 | Map soapRequestBodyMapping; 43 | String soapService; 44 | String soapPort; 45 | ``` 46 | 47 | * ``openAPI``, ``soapService`` and ``soapPort`` are the respective representation of the SOAP endpoint 48 | * ``soapRequestBodyMapping`` contains a map of SOAPRequestElement for all the SOAP operations. 49 | 50 | ``SOAPRequestElement`` Contains following, 51 | 52 | ```java 53 | Document soapRequestBody; 54 | String soapAction; 55 | String soapNamespace; 56 | String nameSpace; 57 | ``` 58 | 59 | ``Document `` contains ```org.w3c.dom.Document``` of the Request message that need to send to the SOAP backend under 60 | ``soapAction``. This Document contains the message with the placeholders for JSON input 61 | 62 | ## License 63 | 64 | ``` 65 | Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 66 | 67 | WSO2 Inc. licenses this file to you under the Apache License, 68 | Version 2.0 (the "License"); you may not use this file except 69 | in compliance with the License. 70 | You may obtain a copy of the License at 71 | 72 | http://www.apache.org/licenses/LICENSE-2.0 73 | 74 | Unless required by applicable law or agreed to in writing, 75 | software distributed under the License is distributed on an 76 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 77 | KIND, either express or implied. See the License for the 78 | specific language governing permissions and limitations 79 | under the License. 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /src/test/resources/complex/nested.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/test/java/org/wso2/soaptorest/SOAPToRESTConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import org.apache.commons.lang3.StringUtils; 21 | import org.junit.jupiter.api.Test; 22 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 23 | import org.wso2.soaptorest.models.SOAPtoRESTConversionData; 24 | 25 | import java.net.MalformedURLException; 26 | import java.net.URL; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; 29 | import static org.junit.jupiter.api.Assertions.assertTrue; 30 | 31 | class SOAPToRESTConverterTest { 32 | 33 | @Test 34 | void testFileGetSOAPtoRESTConversionData() throws SOAPToRESTException { 35 | 36 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData("src/test/resources" + 37 | "/calculator/calculator.wsdl", "Test API", "1.0.0"); 38 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 39 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 4); 40 | assertEquals(soaPtoRESTConversionData.getSoapService(), "CalculatorService"); 41 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "CalculatorPort"); 42 | 43 | } 44 | 45 | @Test 46 | void testURLGetSOAPtoRESTConversionData() throws MalformedURLException, SOAPToRESTException { 47 | 48 | URL url = new URL("https://raw.githubusercontent.com/wso2/soap-to-rest/main/src/test/resources/calculator-remote/calculator.wsdl"); 49 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData(url, "Test API", "1.0.0"); 50 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 51 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 4); 52 | assertEquals(soaPtoRESTConversionData.getSoapService(), "CalculatorService"); 53 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "CalculatorPort"); 54 | 55 | } 56 | 57 | @Test 58 | void testIssue28() throws MalformedURLException, SOAPToRESTException { 59 | 60 | URL url = new URL( 61 | "https://raw.githubusercontent.com/wso2/soap-to-rest/main/src/test/resources/issue-28/failing.wsdl"); 62 | SOAPtoRESTConversionData soaPtoRESTConversionData = 63 | SOAPToRESTConverter.getSOAPtoRESTConversionData(url, "Test API", "1.0.0"); 64 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 65 | assertEquals(1, soaPtoRESTConversionData.getAllSOAPRequestBodies().size()); 66 | assertEquals(soaPtoRESTConversionData.getSoapService(), "myServer"); 67 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "myServerSOAP"); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/models/WSDLSOAPOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.models; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * Extracted SOAP operation details representation. 24 | */ 25 | public class WSDLSOAPOperation { 26 | 27 | private String name; 28 | private String soapBindingOpName; 29 | private String soapAction; 30 | private String targetNamespace; 31 | private String style; 32 | private String messageType; 33 | private String httpVerb; 34 | private List inputParameterModel; 35 | private List outputParameterModel; 36 | 37 | public WSDLSOAPOperation() { 38 | 39 | } 40 | 41 | public String getName() { 42 | 43 | return name; 44 | } 45 | 46 | public void setName(String name) { 47 | 48 | this.name = name; 49 | } 50 | 51 | public String getSoapAction() { 52 | 53 | return soapAction; 54 | } 55 | 56 | public void setSoapAction(String soapAction) { 57 | 58 | this.soapAction = soapAction; 59 | } 60 | 61 | public String getTargetNamespace() { 62 | 63 | return targetNamespace; 64 | } 65 | 66 | public void setTargetNamespace(String targetNamespace) { 67 | 68 | this.targetNamespace = targetNamespace; 69 | } 70 | 71 | public String getStyle() { 72 | 73 | return style; 74 | } 75 | 76 | public void setStyle(String style) { 77 | 78 | this.style = style; 79 | } 80 | 81 | public String getHttpVerb() { 82 | 83 | return httpVerb; 84 | } 85 | 86 | public void setHttpVerb(String httpVerb) { 87 | 88 | this.httpVerb = httpVerb; 89 | } 90 | 91 | 92 | public String getSoapBindingOpName() { 93 | 94 | return soapBindingOpName; 95 | } 96 | 97 | public void setSoapBindingOpName(String soapBindingOpName) { 98 | 99 | this.soapBindingOpName = soapBindingOpName; 100 | } 101 | 102 | public List getInputParameterModel() { 103 | 104 | return inputParameterModel; 105 | } 106 | 107 | public void setInputParameterModel(List inputParameterModel) { 108 | 109 | this.inputParameterModel = inputParameterModel; 110 | } 111 | 112 | public List getOutputParameterModel() { 113 | 114 | return outputParameterModel; 115 | } 116 | 117 | public void setOutputParameterModel(List outputParameterModel) { 118 | 119 | this.outputParameterModel = outputParameterModel; 120 | } 121 | 122 | public String getMessageType() { 123 | 124 | return messageType; 125 | } 126 | 127 | public void setMessageType(String messageType) { 128 | 129 | this.messageType = messageType; 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/utils/SOAPToRESTConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.utils; 19 | 20 | /** 21 | * Constants used for wsdl processing in soap to rest mapping. 22 | */ 23 | public class SOAPToRESTConstants { 24 | 25 | public static final String EMPTY_STRING = ""; 26 | public static final String SOAP_VERSION_11 = "1.1"; 27 | public static final String SOAP_VERSION_12 = "1.2"; 28 | public static final String SOAP11_NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/"; 29 | public static final String SOAP12_NAMESPACE = "http://www.w3.org/2003/05/soap-envelope"; 30 | public static final String X_NAMESPACE_QUALIFIED = "x-namespace-qualified"; 31 | 32 | public static final String QUALIFIED = "qualified"; 33 | public static final String ROOT_ELEMENT_PREFIX = "rootElement_"; 34 | public static final String XMLNS = "xmlns"; 35 | public static final String X_WSO2_UNIQUE_NAMESPACE = "x-wso2-empty-namespace"; 36 | public static final String BASE_CONTENT_KEYWORD = "_base"; 37 | public static final String ATTR_CONTENT_KEYWORD = "_attr"; 38 | public static final String SOAP_RPC_MESSAGE_TYPE = "rpc"; 39 | public static final String DEFAULT_DESCRIPTION = "Default Description"; 40 | public static final String WSDL_VERSION_11 = "1.1"; 41 | 42 | //OAS Constants 43 | public static final String DEFAULT_CONTENT_TYPE = "*/*"; 44 | public static final String SOAP_ACTION = "soap-action"; 45 | public static final String SOAP_OPERATION = "soap-operation"; 46 | public static final String NAMESPACE = "namespace"; 47 | public static final String WSO2_SOAP = "x-wso2-soap"; 48 | public static final String SOAP_VERSION = "x-soap-version"; 49 | public static final String SOAP_MESSAGE_TYPE = "x-soap-message-type"; 50 | public static final String SOAP_STYLE = "x-soap-style"; 51 | 52 | //Sequence Generator Constants 53 | public static final String NAMESPACE_SEPARATOR = ":"; 54 | public static final String PATH_SEPARATOR = "/"; 55 | 56 | public static final String NAMESPACE_PREFIX = "web"; 57 | public static final String ARRAY_PLACEHOLDER = "ARRAY_PLACEHOLDER"; 58 | 59 | // HTTP Constants 60 | public static final String HTTP_METHOD_POST = "POST"; 61 | public static final String HTTP_METHOD_GET = "GET"; 62 | 63 | 64 | //OAS Generator Constants 65 | public static final String OAS_DEFINITIONS_PREFIX = "#/components/schemas/"; 66 | public static final String OAS_DEFINITIONS_ROOT_ELEMENT_PATH = "#/components/schemas/rootElement_"; 67 | public static final String EXTENSION_NAME = "ExtensionObject"; 68 | public static final String OAS_ALL_MEDIA_TYPE = "*/*"; 69 | public static final String OBJECT_TYPE = "object"; 70 | public static final String ARRAY_TYPE = "array"; 71 | public static final String IF_PLACEHOLDER = "ifPlaceholder"; 72 | public static final String ELSE_PLACEHOLDER = "elsePlaceholder"; 73 | public static final String QUESTION_MARK_PLACEHOLDER = "questionPlaceholder"; 74 | public static final String ATTRIBUTE_PLACEHOLDER = "attributePlaceholder"; 75 | public static final String IS_EMPTY_ATTRIBUTE = "addIsEmptyCheck"; 76 | public static final String VALUE_ATTRIBUTE = "valueAttribute"; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/resources/issue-28/failing.wsdl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 37 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/test/resources/calculator/calculator.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/test/resources/calculator-remote/calculator.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/test/java/org/wso2/soaptorest/ComplexWSDLTestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import org.apache.commons.lang3.StringUtils; 21 | import org.junit.jupiter.api.Test; 22 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 23 | import org.wso2.soaptorest.models.SOAPRequestElement; 24 | import org.wso2.soaptorest.models.SOAPtoRESTConversionData; 25 | 26 | import javax.xml.transform.OutputKeys; 27 | import javax.xml.transform.Transformer; 28 | import javax.xml.transform.TransformerException; 29 | import javax.xml.transform.TransformerFactory; 30 | import javax.xml.transform.dom.DOMSource; 31 | import javax.xml.transform.stream.StreamResult; 32 | import java.io.StringWriter; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Set; 36 | 37 | import static org.junit.jupiter.api.Assertions.assertEquals; 38 | import static org.junit.jupiter.api.Assertions.assertTrue; 39 | 40 | class ComplexWSDLTestCase { 41 | 42 | @Test 43 | void testNestedWSDL() throws SOAPToRESTException { 44 | 45 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData("src/test/resources" + 46 | "/complex/nested.wsdl", "Test API", "1.0.0"); 47 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 48 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 1); 49 | assertEquals(soaPtoRESTConversionData.getSoapService(), "nestedSampleService"); 50 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "nestedSamplePort"); 51 | 52 | } 53 | 54 | @Test 55 | void testArrayWSDL() throws SOAPToRESTException, TransformerException { 56 | 57 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData("src/test/resources" + 58 | "/complex/arrays.wsdl", "Test API", "1.0.0"); 59 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 60 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 1); 61 | assertEquals(soaPtoRESTConversionData.getSoapService(), "arraysSampleService"); 62 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "arraysSamplePort"); 63 | 64 | Set> soapElementEntry = soaPtoRESTConversionData.getAllSOAPRequestBodies(); 65 | for (Map.Entry requestElementEntry : soapElementEntry) { 66 | SOAPRequestElement soapRequestElement = requestElementEntry.getValue(); 67 | StringWriter writer = new StringWriter(); 68 | TransformerFactory tf = TransformerFactory.newInstance(); 69 | Transformer transformer = tf.newTransformer(); 70 | transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 71 | 72 | transformer.transform(new DOMSource(soapRequestElement.getSoapRequestBody()), 73 | new StreamResult(writer)); 74 | String soapPayload = writer.getBuffer().toString(); 75 | String complexInnerType = ""; 76 | assertTrue(soapPayload.contains(complexInnerType)); 77 | assertEquals("http://example.com/", soapRequestElement.getNamespace()); 78 | assertEquals("http://schemas.xmlsoap.org/soap/envelope/", soapRequestElement.getSoapNamespace()); 79 | assertEquals("", soapRequestElement.getSoapAction()); 80 | } 81 | } 82 | 83 | @Test 84 | void testGroupWSDL() throws SOAPToRESTException { 85 | 86 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData("src/test/resources" + 87 | "/complex/groups.wsdl", "Test API", "1.0.0"); 88 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 89 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 1); 90 | assertEquals(soaPtoRESTConversionData.getSoapService(), "groupsSampleService"); 91 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "groupsSamplePort"); 92 | } 93 | 94 | @Test 95 | void testChoiceWSDL() throws SOAPToRESTException { 96 | 97 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData("src/test/resources" + 98 | "/complex/choice.wsdl", "Test API", "1.0.0"); 99 | assertTrue(StringUtils.isNotBlank(soaPtoRESTConversionData.getOASString())); 100 | assertEquals(soaPtoRESTConversionData.getAllSOAPRequestBodies().size(), 1); 101 | assertEquals(soaPtoRESTConversionData.getSoapService(), "choiceSampleService"); 102 | assertEquals(soaPtoRESTConversionData.getSoapPort(), "choiceSamplePort"); 103 | } 104 | 105 | @Test 106 | void testWSDLWithOptional() throws SOAPToRESTException { 107 | SOAPtoRESTConversionData soaPtoRESTConversionData = SOAPToRESTConverter.getSOAPtoRESTConversionData( 108 | "src/test/resources" + "/complex/nested.wsdl", "Test API", "1.0.0"); 109 | Set> soapElementEntry = soaPtoRESTConversionData.getAllSOAPRequestBodies(); 110 | List requiredValues = soaPtoRESTConversionData.getOpenAPI().getComponents() 111 | .getSchemas().get("innerType2").getRequired(); 112 | assertTrue(requiredValues.contains("inner3")); 113 | assertTrue(requiredValues.contains("int3")); 114 | requiredValues = soaPtoRESTConversionData.getOpenAPI().getComponents().getSchemas() 115 | .get("FCUBS_HEADERType").getRequired(); 116 | assertTrue(requiredValues.get(0).equals("PASSWORD")); 117 | } 118 | } -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/utils/ListJSONPaths.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.utils; 19 | 20 | import io.swagger.v3.oas.models.OpenAPI; 21 | import org.apache.commons.lang3.StringUtils; 22 | import org.json.JSONArray; 23 | import org.json.JSONObject; 24 | 25 | import io.swagger.oas.inflector.examples.models.ArrayExample; 26 | import io.swagger.oas.inflector.examples.models.Example; 27 | import io.swagger.oas.inflector.examples.models.ObjectExample; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Iterator; 31 | import java.util.List; 32 | import java.util.Map; 33 | 34 | /** 35 | * Utility class to extract all the json paths of the given json string 36 | */ 37 | public class ListJSONPaths { 38 | 39 | /** 40 | * This method will return a list of all the available json paths of the input json string 41 | * 42 | * @param json input json file 43 | * @return the arraylist of the available json paths 44 | */ 45 | public static ArrayList getJsonPaths(String json) { 46 | 47 | ArrayList pathList = new ArrayList<>(); 48 | JSONObject object = new JSONObject(json); 49 | if (json != JSONObject.NULL) { 50 | readObject(object, null, pathList); 51 | } 52 | return pathList; 53 | } 54 | 55 | private static void readObject(JSONObject object, String jsonPath, ArrayList pathList) { 56 | 57 | Iterator keysItr = object.keys(); 58 | String parentPath = jsonPath; 59 | if (keysItr.hasNext()) { 60 | while (keysItr.hasNext()) { 61 | String key = (String) keysItr.next(); 62 | Object value = object.get(key); 63 | jsonPath = (jsonPath == null) ? key : parentPath + "." + key; 64 | 65 | if (value instanceof JSONArray) { 66 | readArray((JSONArray) value, jsonPath, pathList); 67 | } else if (value instanceof JSONObject) { 68 | readObject((JSONObject) value, jsonPath, pathList); 69 | } else { 70 | pathList.add(jsonPath); 71 | } 72 | } 73 | } else if (StringUtils.isNotBlank(jsonPath)) { 74 | pathList.add(jsonPath); 75 | } 76 | } 77 | 78 | private static void readArray(JSONArray array, String jsonPath, ArrayList pathList) { 79 | 80 | String parentPath = jsonPath; 81 | for (int i = 0; i < array.length(); i++) { 82 | Object value = array.get(i); 83 | jsonPath = parentPath + "[" + i + "]"; 84 | 85 | if (value instanceof JSONArray) { 86 | readArray((JSONArray) value, jsonPath, pathList); 87 | } else if (value instanceof JSONObject) { 88 | readObject((JSONObject) value, jsonPath, pathList); 89 | } else { 90 | pathList.add(jsonPath); 91 | } 92 | } 93 | } 94 | 95 | /* 96 | * This method will return a list of all the available json paths of the input json string 97 | * @param example example object 98 | * @return the arraylist of the available json paths 99 | */ 100 | public static ArrayList getJsonPathsFromExample(Example example, Map jsonPathSchemaMapping) { 101 | ArrayList pathList = new ArrayList<>(); 102 | if (example != null) { 103 | listExamples("", example, pathList, jsonPathSchemaMapping); 104 | } 105 | return pathList; 106 | } 107 | 108 | /* 109 | * This method will return a list of all the available json paths of the input json string 110 | * @param parent parent json path 111 | * @param example example object 112 | * @param parameterJsonPathMapping list of json paths 113 | * @return the arraylist of the available json paths 114 | */ 115 | public static void listExamples(String parent, Example example, ArrayList parameterJsonPathMapping 116 | , Map jsonPathSchemaMapping) { 117 | if (example != null) { 118 | if (SOAPToRESTConstants.OBJECT_TYPE.equals(example.getTypeName())) { 119 | Map values = ((ObjectExample) example).getValues(); 120 | if (values != null) { 121 | for (Map.Entry entry : values.entrySet()) { 122 | String childKey = parent.isEmpty() ? entry.getKey() : parent + "." + entry.getKey(); 123 | if (entry.getValue() != null && entry.getValue().getName() != null) { 124 | jsonPathSchemaMapping.put(childKey, entry.getValue().getName()); 125 | } 126 | listExamples(childKey, entry.getValue(), parameterJsonPathMapping, jsonPathSchemaMapping); 127 | } 128 | } else if (StringUtils.isNotBlank(parent)) { 129 | parameterJsonPathMapping.add(parent); 130 | } 131 | } else if (SOAPToRESTConstants.ARRAY_TYPE.equals(example.getTypeName())) { 132 | List exampleArray = ((ArrayExample) example).getItems(); 133 | String jsonPath; 134 | if (exampleArray.size() > 0) { 135 | for (int i = 0; i < exampleArray.size(); i++) { 136 | Example exampleItem = exampleArray.get(i); 137 | jsonPath = parent + "[" + i + "]"; 138 | if (SOAPToRESTConstants.OBJECT_TYPE.equals(exampleItem.getTypeName())) { 139 | listExamples(jsonPath, exampleItem, parameterJsonPathMapping, jsonPathSchemaMapping); 140 | } else if (SOAPToRESTConstants.ARRAY_TYPE.equals(exampleItem.getTypeName())) { 141 | listExamples(jsonPath, exampleItem, parameterJsonPathMapping, jsonPathSchemaMapping); 142 | } else { 143 | parameterJsonPathMapping.add(jsonPath); 144 | } 145 | } 146 | } 147 | } else if (StringUtils.isNotBlank(parent)) { 148 | parameterJsonPathMapping.add(parent); 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.wso2 6 | wso2 7 | 1.4 8 | 9 | org.wso2 10 | soaptorest 11 | 1.8.1-SNAPSHOT 12 | 13 | scm:git:https://github.com/wso2/soap-to-rest.git 14 | scm:git:https://github.com/wso2/soap-to-rest.git 15 | https://github.com/wso2/soap-to-rest.git 16 | HEAD 17 | 18 | 19 | 20 | xerces.wso2 21 | xercesImpl 22 | ${xerces.wso2.version} 23 | 24 | 25 | wsdl4j 26 | wsdl4j 27 | ${org.wsdl4j.version} 28 | 29 | 30 | org.apache.woden.wso2 31 | woden 32 | ${apache.woden.version} 33 | 34 | 35 | org.wso2.orbit.io.swagger.v3 36 | swagger-parser 37 | ${swagger.parser.v3.version} 38 | 39 | 40 | org.wso2.orbit.io.swagger 41 | swagger-inflector-oas3 42 | ${swagger.inflector.oas3.version} 43 | 44 | 45 | org.json.wso2 46 | json 47 | ${json.orbit.version} 48 | 49 | 50 | org.junit.jupiter 51 | junit-jupiter 52 | ${junit.version} 53 | test 54 | 55 | 56 | 57 | 8 58 | 8 59 | 2.8.1.wso2v2 60 | 1.6.2 61 | 1.0.0.M9-wso2v1 62 | 2.0.30.wso2v1 63 | 2.0.5.wso2v1 64 | 3.0.0.wso2v1 65 | 5.8.2 66 | 67 | 68 | 69 | wso2-nexus 70 | WSO2 internal Repository 71 | https://maven.wso2.org/nexus/content/groups/wso2-public/ 72 | 73 | true 74 | daily 75 | ignore 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-release-plugin 84 | 85 | clean install 86 | true 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-deploy-plugin 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-compiler-plugin 96 | 97 | ${maven.compiler.source} 98 | ${maven.compiler.target} 99 | 100 | 101 | 102 | org.jacoco 103 | jacoco-maven-plugin 104 | 0.8.4 105 | 106 | 107 | default-prepare-agent-by-coverage-enforcer 108 | 109 | prepare-agent 110 | 111 | 112 | argLine 113 | 114 | 115 | 116 | default-report-by-coverage-enforcer 117 | 118 | report 119 | 120 | 121 | ${project.build.directory}/jacoco.exec 122 | 123 | 124 | 125 | default-check-by-coverage-enforcer 126 | 127 | check 128 | 129 | 130 | ${project.build.directory}/jacoco.exec 131 | 132 | 133 | 134 | BUNDLE 135 | 136 | 137 | 138 | LINE 139 | COVEREDRATIO 140 | 0.0 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | maven-surefire-plugin 151 | 2.22.2 152 | 153 | ${argLine} 154 | 155 | 156 | 157 | maven-failsafe-plugin 158 | 2.22.2 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-assembly-plugin 163 | 3.1.1 164 | 165 | 166 | 167 | jar-with-dependencies 168 | 169 | 170 | 171 | 172 | 173 | make-assembly 174 | package 175 | 176 | single 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | wso2.releases 187 | WSO2 internal Repository 188 | https://maven.wso2.org/nexus/content/repositories/releases/ 189 | 190 | true 191 | daily 192 | ignore 193 | 194 | 195 | 196 | wso2.snapshots 197 | Apache Snapshot Repository 198 | https://maven.wso2.org/nexus/content/repositories/snapshots/ 199 | 200 | true 201 | daily 202 | 203 | 204 | false 205 | 206 | 207 | 208 | wso2-nexus 209 | WSO2 internal Repository 210 | https://maven.wso2.org/nexus/content/groups/wso2-public/ 211 | 212 | true 213 | daily 214 | ignore 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/utils/WSDLProcessingUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.utils; 19 | 20 | import com.ibm.wsdl.extensions.schema.SchemaReferenceImpl; 21 | import org.apache.ws.commons.schema.XmlSchema; 22 | import org.apache.ws.commons.schema.XmlSchemaCollection; 23 | import org.apache.xerces.impl.Constants; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.w3c.dom.Document; 27 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 28 | import org.xml.sax.SAXException; 29 | 30 | import javax.wsdl.Definition; 31 | import javax.wsdl.Types; 32 | import javax.wsdl.extensions.schema.Schema; 33 | import javax.wsdl.extensions.schema.SchemaImport; 34 | import javax.xml.XMLConstants; 35 | import javax.xml.parsers.DocumentBuilder; 36 | import javax.xml.parsers.DocumentBuilderFactory; 37 | import javax.xml.parsers.ParserConfigurationException; 38 | import java.io.FileInputStream; 39 | import java.io.IOException; 40 | import java.io.InputStream; 41 | import java.net.URL; 42 | import java.util.*; 43 | 44 | /** 45 | * Utility class to read the WSDL file from URL or file system safely and read all imported XSD Schemas 46 | * and return them as {@link Document} and Set of {@link XmlSchema} 47 | */ 48 | public class WSDLProcessingUtil { 49 | 50 | static Logger log = LoggerFactory.getLogger(WSDLProcessingUtil.class); 51 | 52 | /** 53 | * Returns a secured document builder to avoid XXE attacks 54 | * 55 | * @return secured document builder to avoid XXE attacks 56 | */ 57 | private static DocumentBuilderFactory getSecuredDocumentBuilder() { 58 | 59 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 60 | dbf.setNamespaceAware(true); 61 | dbf.setXIncludeAware(false); 62 | dbf.setExpandEntityReferences(false); 63 | try { 64 | dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false); 65 | dbf.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false); 66 | dbf.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false); 67 | dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 68 | } catch (ParserConfigurationException e) { 69 | // Skip throwing the error as this exception doesn't break actual DocumentBuilderFactory creation 70 | log.error("Failed to load XML Processor Feature " + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE + " or " 71 | + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE + " or " + Constants.LOAD_EXTERNAL_DTD_FEATURE, e); 72 | } 73 | return dbf; 74 | } 75 | 76 | /** 77 | * Returns an "XXE safe" built DOM XML object by reading the content from the provided file path. 78 | * 79 | * @param path path to fetch the content 80 | * @return an "XXE safe" built DOM XML object by reading the content from the provided file path 81 | * @throws SOAPToRESTException When error occurred while reading from file path 82 | */ 83 | public static Document getSecuredParsedDocumentFromPath(String path, String systemId) throws SOAPToRESTException, IOException { 84 | 85 | InputStream inputStream = null; 86 | try { 87 | DocumentBuilderFactory factory = getSecuredDocumentBuilder(); 88 | DocumentBuilder builder = factory.newDocumentBuilder(); 89 | inputStream = new FileInputStream(path); 90 | return builder.parse(inputStream, systemId); 91 | } catch (ParserConfigurationException | IOException | SAXException e) { 92 | throw new SOAPToRESTException("Error while reading WSDL document", e); 93 | } finally { 94 | if (inputStream != null) { 95 | inputStream.close(); 96 | } 97 | } 98 | } 99 | 100 | public static Document getSecuredParsedDocumentFromURL(URL url) throws SOAPToRESTException, IOException { 101 | 102 | InputStream inputStream = null; 103 | try { 104 | DocumentBuilderFactory factory = getSecuredDocumentBuilder(); 105 | DocumentBuilder builder = factory.newDocumentBuilder(); 106 | inputStream = url.openStream(); 107 | return builder.parse(inputStream); 108 | } catch (ParserConfigurationException | IOException | SAXException e) { 109 | throw new SOAPToRESTException("Error while reading WSDL document", e); 110 | } finally { 111 | if (inputStream != null) { 112 | inputStream.close(); 113 | } 114 | } 115 | } 116 | 117 | /** 118 | * This method will read the given WSDL definition and extract all the referred XSD files and create List of 119 | * XSD Schemas 120 | * 121 | * @param wsdlDefinition input WSDL definition 122 | * @return returns Set of {@link XmlSchema} objects of all the referred XSDs of the WSDL file 123 | */ 124 | public static Set getXMLSchemasFromWSDL(Definition wsdlDefinition, String systemId) { 125 | 126 | Set schemaArrayList = new HashSet<>(); 127 | Types types = wsdlDefinition.getTypes(); 128 | List typeList = new ArrayList<>(); 129 | if (types != null) { 130 | typeList = types.getExtensibilityElements(); 131 | } 132 | if (typeList != null) { 133 | for (Object ext : typeList) { 134 | if (ext instanceof Schema) { 135 | Schema schema = (Schema) ext; 136 | XmlSchemaCollection schemaCollection = new XmlSchemaCollection(); 137 | schemaArrayList.add(schemaCollection.read(schema.getElement(), systemId)); 138 | // Process imported XSDs if available 139 | Map importedSchemas = schema.getImports(); 140 | if (importedSchemas != null) { 141 | processImportedSchemas(importedSchemas, schemaArrayList); 142 | } 143 | List schemaIncludes = schema.getIncludes(); 144 | if (schemaIncludes != null) { 145 | processIncludedSchemas(schemaIncludes, schemaArrayList); 146 | } 147 | } 148 | } 149 | } 150 | return schemaArrayList; 151 | } 152 | 153 | private static void processImportedSchemas(Map importedSchemas, Set schemaArrayList) { 154 | 155 | for (Object importedSchemaObj : importedSchemas.keySet()) { 156 | String schemaUrl = (String) importedSchemaObj; 157 | if (importedSchemas.get(schemaUrl) != null) { 158 | Vector vector = (Vector) importedSchemas.get(schemaUrl); 159 | for (Object schemaVector : vector) { 160 | if (schemaVector instanceof SchemaImport) { 161 | Schema referencedSchema = ((SchemaImport) schemaVector).getReferencedSchema(); 162 | if (referencedSchema != null && referencedSchema.getElement() != null) { 163 | String systemId = referencedSchema.getDocumentBaseURI(); 164 | XmlSchemaCollection schemaCollection = new XmlSchemaCollection(); 165 | schemaArrayList.add(schemaCollection.read(referencedSchema.getElement(), systemId)); 166 | Map nestedImportedSchemas = referencedSchema.getImports(); 167 | if (nestedImportedSchemas != null) { 168 | processImportedSchemas(nestedImportedSchemas, schemaArrayList); 169 | } 170 | List nestedSchemaIncludes = referencedSchema.getIncludes(); 171 | if (nestedSchemaIncludes != null) { 172 | processIncludedSchemas(nestedSchemaIncludes, schemaArrayList); 173 | } 174 | } else { 175 | log.warn("Cannot access referenced schema for the schema defined at: " + schemaUrl); 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | 183 | private static void processIncludedSchemas(List schemaIncludes, Set schemaArrayList) { 184 | 185 | for (Object includedSchemaRef : schemaIncludes) { 186 | if (includedSchemaRef instanceof SchemaReferenceImpl) { 187 | Schema referencedSchema = ((SchemaReferenceImpl) includedSchemaRef).getReferencedSchema(); 188 | 189 | if (referencedSchema != null && referencedSchema.getElement() != null) { 190 | String systemId = referencedSchema.getDocumentBaseURI(); 191 | XmlSchemaCollection schemaCollection = new XmlSchemaCollection(); 192 | schemaArrayList.add(schemaCollection.read(referencedSchema.getElement(), systemId)); 193 | Map nestedImportedSchemas = referencedSchema.getImports(); 194 | if (nestedImportedSchemas != null) { 195 | processImportedSchemas(nestedImportedSchemas, schemaArrayList); 196 | } 197 | List nestedSchemaIncludes = referencedSchema.getIncludes(); 198 | if (nestedSchemaIncludes != null) { 199 | processIncludedSchemas(nestedSchemaIncludes, schemaArrayList); 200 | } 201 | } 202 | } 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/WSDLProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import org.apache.ws.commons.schema.*; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 24 | import org.wso2.soaptorest.models.*; 25 | import org.wso2.soaptorest.utils.SOAPToRESTConstants; 26 | import org.wso2.soaptorest.utils.WSDLProcessingUtil; 27 | 28 | import javax.wsdl.Definition; 29 | import javax.wsdl.WSDLException; 30 | import javax.wsdl.factory.WSDLFactory; 31 | import javax.wsdl.xml.WSDLReader; 32 | import javax.xml.namespace.QName; 33 | import java.io.File; 34 | import java.io.IOException; 35 | import java.net.URL; 36 | import java.util.ArrayList; 37 | import java.util.Iterator; 38 | import java.util.List; 39 | import java.util.Set; 40 | 41 | /** 42 | * The class that processes WSDL 1.1 documents 43 | */ 44 | public class WSDLProcessor { 45 | 46 | private static final String JAVAX_WSDL_VERBOSE_MODE = "javax.wsdl.verbose"; 47 | private static final String JAVAX_WSDL_IMPORT_DOCUMENTS = "javax.wsdl.importDocuments"; 48 | private static volatile WSDLFactory wsdlFactoryInstance; 49 | Logger log = LoggerFactory.getLogger(WSDLProcessor.class); 50 | Set wsdlSchemaList; 51 | List xsdDataModels = new ArrayList<>(); 52 | private Definition wsdlDefinition; 53 | 54 | public static WSDLFactory getWsdlFactoryInstance() throws SOAPToRESTException { 55 | 56 | if (wsdlFactoryInstance == null) { 57 | try { 58 | synchronized (WSDLProcessor.class) { 59 | if (wsdlFactoryInstance == null) { 60 | wsdlFactoryInstance = WSDLFactory.newInstance(); 61 | } 62 | } 63 | } catch (WSDLException e) { 64 | throw new SOAPToRESTException("Error while instantiating WSDL 1.1 factory", e); 65 | } 66 | } 67 | return wsdlFactoryInstance; 68 | } 69 | 70 | /** 71 | * Initialize the processor based on a provided file path which contains WSDL file. 72 | * 73 | * @param path File path with WSDL file 74 | * @throws SOAPToRESTException Unexpected error while initialization 75 | */ 76 | void init(String path) throws SOAPToRESTException { 77 | 78 | WSDLReader wsdlReader = getWsdlFactoryInstance().newWSDLReader(); 79 | 80 | //Switch off the verbose mode 81 | wsdlReader.setFeature(JAVAX_WSDL_VERBOSE_MODE, false); 82 | wsdlReader.setFeature(JAVAX_WSDL_IMPORT_DOCUMENTS, false); 83 | try { 84 | File file = new File(path); 85 | String systemId = file.getParent(); 86 | if (systemId == null) { 87 | systemId = ""; 88 | } 89 | wsdlDefinition = wsdlReader.readWSDL(systemId, WSDLProcessingUtil.getSecuredParsedDocumentFromPath(path, systemId)); 90 | initializeModels(wsdlDefinition, systemId); 91 | if (log.isDebugEnabled()) { 92 | log.debug("Successfully initialized the WSDL File from given path"); 93 | } 94 | } catch (WSDLException | IOException e) { 95 | //This implementation class cannot process the WSDL. 96 | log.debug("Cannot process the WSDL by " + this.getClass().getName(), e); 97 | throw new SOAPToRESTException("Cannot process the provide WSDL file", e); 98 | } 99 | } 100 | 101 | /** 102 | * Initialize the processor based on a provided URL of the WSDL file. 103 | * 104 | * @param url URL of the WSDL file 105 | * @throws SOAPToRESTException Unexpected error while initialization 106 | */ 107 | void init(URL url) throws SOAPToRESTException { 108 | 109 | WSDLReader wsdlReader = getWsdlFactoryInstance().newWSDLReader(); 110 | 111 | //Switch off the verbose mode 112 | wsdlReader.setFeature(JAVAX_WSDL_VERBOSE_MODE, false); 113 | wsdlReader.setFeature(JAVAX_WSDL_IMPORT_DOCUMENTS, false); 114 | try { 115 | wsdlDefinition = wsdlReader.readWSDL(url.toString(), 116 | WSDLProcessingUtil.getSecuredParsedDocumentFromURL(url)); 117 | initializeModels(wsdlDefinition, null); 118 | if (log.isDebugEnabled()) { 119 | log.debug("Successfully initialized the WSDL File from given URL"); 120 | } 121 | } catch (WSDLException | IOException e) { 122 | //This implementation class cannot process the WSDL. 123 | log.debug("Cannot process the WSDL by " + this.getClass().getName(), e); 124 | throw new SOAPToRESTException("Cannot process the provide WSDL file", e); 125 | } 126 | } 127 | 128 | /** 129 | * This method will extract the XSD Schemas from the WSDL file and generate the data model 130 | * 131 | * @param wsdlDefinition input WSDL definition 132 | */ 133 | public void initializeModels(Definition wsdlDefinition, String systemId) { 134 | 135 | wsdlSchemaList = WSDLProcessingUtil.getXMLSchemasFromWSDL(wsdlDefinition, systemId); 136 | for (XmlSchema xmlSchema : wsdlSchemaList) { 137 | // Process single XSD Schema file from the available schema list 138 | XSModel xsModel = new XSModel(); 139 | xsModel.setTargetNamespace(xmlSchema.getTargetNamespace()); 140 | xsModel.setElementFormDefaultQualified( 141 | SOAPToRESTConstants.QUALIFIED.equals(xmlSchema.getElementFormDefault().getValue())); 142 | 143 | //Process Elements in the XSD 144 | Iterator elementsIterator = xmlSchema.getElements().getValues(); 145 | while (elementsIterator.hasNext()) { 146 | Object xmlSchemaObject = elementsIterator.next(); 147 | if (xmlSchemaObject instanceof XmlSchemaElement) { 148 | XSElement xsElement = processXmlSchemaElement((XmlSchemaElement) xmlSchemaObject); 149 | xsModel.addElement(xsElement); 150 | } 151 | } 152 | 153 | //Process Attributes in the XSD 154 | Iterator attributesIterator = xmlSchema.getAttributes().getValues(); 155 | while (attributesIterator.hasNext()) { 156 | Object xmlSchemaObject = attributesIterator.next(); 157 | if (xmlSchemaObject instanceof XmlSchemaAttribute) { 158 | XSAttribute xsAttribute = processXmlSchemaAttribute((XmlSchemaAttribute) xmlSchemaObject); 159 | xsModel.addAttribute(xsAttribute); 160 | } 161 | } 162 | 163 | //Process XSD Group data type 164 | Iterator groupIterator = xmlSchema.getGroups().getValues(); 165 | while (groupIterator.hasNext()) { 166 | XSSequence xsSequence = new XSSequence(); 167 | XSGroup xsGroup = new XSGroup(); 168 | Object schemaGroupObject = groupIterator.next(); 169 | if (schemaGroupObject instanceof XmlSchemaGroup) { 170 | XmlSchemaGroup xmlSchemaGroup = (XmlSchemaGroup) schemaGroupObject; 171 | xsGroup.setName(xmlSchemaGroup.getName()); 172 | int numOfGroupElements = xmlSchemaGroup.getParticle().getItems().getCount(); 173 | for (int i = 0; i < numOfGroupElements; i++) { 174 | XmlSchemaObject xmlSchemaObject = xmlSchemaGroup.getParticle().getItems().getItem(i); 175 | if (xmlSchemaObject instanceof XmlSchemaElement) { 176 | XSElement xsElement = processXmlSchemaElement((XmlSchemaElement) xmlSchemaObject); 177 | xsSequence.addElement(xsElement); 178 | } 179 | } 180 | xsGroup.addSequence(xsSequence); 181 | } 182 | xsModel.addGroup(xsGroup); 183 | } 184 | 185 | //Process Data Types defined in the schema 186 | Iterator schemaTypeIterator = xmlSchema.getSchemaTypes().getValues(); 187 | while (schemaTypeIterator.hasNext()) { 188 | Object schemaTypeObject = schemaTypeIterator.next(); 189 | if (schemaTypeObject instanceof XmlSchemaType) { 190 | XSDataType xsDataType = processXSDataType((XmlSchemaType) schemaTypeObject); 191 | xsModel.addXSDataType(xsDataType); 192 | } 193 | 194 | } 195 | xsdDataModels.add(xsModel); 196 | } 197 | } 198 | 199 | private XSElement processXmlSchemaElement(XmlSchemaElement xmlSchemaElement) { 200 | 201 | XSElement xsElement = new XSElement(); 202 | xsElement.setName(xmlSchemaElement.getQName()); 203 | xsElement.setArray(xmlSchemaElement.getMaxOccurs() > 1); 204 | if (xmlSchemaElement.getSchemaTypeName() != null) { 205 | xsElement.setType(xmlSchemaElement.getSchemaTypeName()); 206 | } else if (xmlSchemaElement.getSchemaType() != null) { 207 | // If the schema type is inline, then the Schema type name will be null 208 | XSDataType inlineComplexType = processXSDataType(xmlSchemaElement.getSchemaType()); 209 | xsElement.setInlineComplexType(inlineComplexType); 210 | } else if (xmlSchemaElement.getRefName() != null) { 211 | xsElement.setRefKey(xmlSchemaElement.getRefName()); 212 | } else { 213 | log.warn("Data type for the child element " + xmlSchemaElement.getName() + "did " + "not processed"); 214 | } 215 | if (xmlSchemaElement.getMinOccurs() == 0) { 216 | xsElement.setOptional(true); 217 | } 218 | return xsElement; 219 | } 220 | 221 | public XSDataType processXSDataType(XmlSchemaType xmlSchemaType) { 222 | 223 | XSDataType xsDataType = new XSDataType(); 224 | if (xmlSchemaType.getName() != null) { 225 | xsDataType.setName(xmlSchemaType.getQName()); 226 | } 227 | if (xmlSchemaType instanceof XmlSchemaComplexType) { 228 | XmlSchemaComplexType complexType = (XmlSchemaComplexType) xmlSchemaType; 229 | 230 | //Process SimpleContent and ComplexContent 231 | XmlSchemaContentModel xmlSchemaContentModel = complexType.getContentModel(); 232 | if (xmlSchemaContentModel != null) { 233 | XmlSchemaContent xmlSchemaContent = xmlSchemaContentModel.getContent(); 234 | if (xmlSchemaContent instanceof XmlSchemaSimpleContentExtension) { 235 | XmlSchemaSimpleContentExtension simpleContentExtension = 236 | (XmlSchemaSimpleContentExtension) xmlSchemaContent; 237 | xsDataType.setExtensionBase(simpleContentExtension.getBaseTypeName()); 238 | } else if (xmlSchemaContent instanceof XmlSchemaComplexContentExtension) { 239 | XmlSchemaComplexContentExtension complexContentExtension = 240 | (XmlSchemaComplexContentExtension) xmlSchemaContent; 241 | xsDataType.setExtensionBase(complexContentExtension.getBaseTypeName()); 242 | } 243 | } 244 | 245 | // Process child elements of the complexTypes 246 | XmlSchemaParticle xmlSchemaParticle = complexType.getParticle(); 247 | // Process XSD All and Sequence as single type since no need to support sequence in openAPI 248 | if (xmlSchemaParticle instanceof XmlSchemaAll || xmlSchemaParticle instanceof XmlSchemaSequence) { 249 | XmlSchemaGroupBase xmlSchemaGroupBase = (XmlSchemaGroupBase) complexType.getParticle(); 250 | int numElements = xmlSchemaGroupBase.getItems().getCount(); 251 | XSSequence xsSequence = new XSSequence(); 252 | for (int i = 0; i < numElements; i++) { 253 | XmlSchemaObject xmlSchemaObject = xmlSchemaGroupBase.getItems().getItem(i); 254 | if (xmlSchemaObject instanceof XmlSchemaElement) { 255 | XSElement xsElement = processXmlSchemaElement((XmlSchemaElement) xmlSchemaObject); 256 | xsSequence.addElement(xsElement); 257 | } 258 | } 259 | xsDataType.setSequence(xsSequence); 260 | } 261 | 262 | //Process Choice types 263 | if (xmlSchemaParticle instanceof XmlSchemaChoice) { 264 | XmlSchemaChoice xmlSchemaChoice = (XmlSchemaChoice) xmlSchemaParticle; 265 | int numElements = xmlSchemaChoice.getItems().getCount(); 266 | XSChoice xsChoice = new XSChoice(); 267 | for (int i = 0; i < numElements; i++) { 268 | XmlSchemaObject xmlSchemaObject = xmlSchemaChoice.getItems().getItem(i); 269 | if (xmlSchemaObject instanceof XmlSchemaElement) { 270 | XSElement xsElement = processXmlSchemaElement((XmlSchemaElement) xmlSchemaObject); 271 | xsChoice.addElement(xsElement); 272 | } 273 | } 274 | xsDataType.setChoice(xsChoice); 275 | } 276 | 277 | //Process Group types 278 | if (xmlSchemaParticle instanceof XmlSchemaGroupRef) { 279 | XmlSchemaGroupRef xmlSchemaGroupRef = (XmlSchemaGroupRef) xmlSchemaParticle; 280 | XSGroup xsGroup = new XSGroup(); 281 | xsGroup.setRefKey(xmlSchemaGroupRef.getRefName()); 282 | xsDataType.setGroup(xsGroup); 283 | } 284 | } 285 | 286 | if (xmlSchemaType instanceof XmlSchemaSimpleType) { 287 | XmlSchemaSimpleType xmlSchemaTypeObject = (XmlSchemaSimpleType) xmlSchemaType; 288 | XSSequence xsSequence = new XSSequence(); 289 | XSElement xsElement = new XSElement(); 290 | xsElement.setName(xmlSchemaTypeObject.getQName()); 291 | if (xmlSchemaTypeObject.getBaseSchemaTypeName() != null) { 292 | xsElement.setType(xmlSchemaTypeObject.getBaseSchemaTypeName()); 293 | } else if (xmlSchemaTypeObject.getContent() != null) { 294 | if (xmlSchemaTypeObject.getContent() instanceof XmlSchemaSimpleTypeRestriction) { 295 | XmlSchemaSimpleTypeRestriction xmlSchemaSimpleTypeRestriction = 296 | (XmlSchemaSimpleTypeRestriction) xmlSchemaTypeObject.getContent(); 297 | xsElement.setType(xmlSchemaSimpleTypeRestriction.getBaseTypeName()); 298 | } 299 | } 300 | if (xsElement.getType() == null) { 301 | xsElement.setType(new QName("string")); 302 | 303 | } 304 | xsSequence.addElement(xsElement); 305 | xsDataType.setSequence(xsSequence); 306 | xsDataType.setSimpleType(true); 307 | } 308 | return xsDataType; 309 | } 310 | 311 | private XSAttribute processXmlSchemaAttribute(XmlSchemaAttribute xmlSchemaAttribute) { 312 | 313 | XSAttribute xsAttribute = new XSAttribute(); 314 | xsAttribute.setName(xmlSchemaAttribute.getQName()); 315 | if (xmlSchemaAttribute.getSchemaTypeName() != null) { 316 | xsAttribute.setType(xmlSchemaAttribute.getSchemaTypeName()); 317 | } else if (xmlSchemaAttribute.getSchemaType() != null) { 318 | // If the schema type is inline, then the Schema type name will be null 319 | XSDataType inlineComplexType = processXSDataType(xmlSchemaAttribute.getSchemaType()); 320 | xsAttribute.setInlineComplexType(inlineComplexType); 321 | } else if (xmlSchemaAttribute.getRefName() != null) { 322 | xsAttribute.setRefKey(xmlSchemaAttribute.getRefName()); 323 | } else { 324 | log.warn("Data type for the child element " + xmlSchemaAttribute.getName() + "did " + "not processed"); 325 | } 326 | return xsAttribute; 327 | } 328 | 329 | public Definition getWsdlDefinition() { 330 | 331 | return wsdlDefinition; 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/utils/SOAPOperationExtractingUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest.utils; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 23 | import org.wso2.soaptorest.models.WSDLInfo; 24 | import org.wso2.soaptorest.models.WSDLParameter; 25 | import org.wso2.soaptorest.models.WSDLSOAPOperation; 26 | 27 | import javax.wsdl.*; 28 | import javax.wsdl.extensions.soap.SOAPBinding; 29 | import javax.wsdl.extensions.soap.SOAPOperation; 30 | import javax.wsdl.extensions.soap12.SOAP12Binding; 31 | import javax.wsdl.extensions.soap12.SOAP12Operation; 32 | import java.util.*; 33 | 34 | /** 35 | * Utility class to extract the SOAP Operations from the given WSDL Definition file 36 | */ 37 | public class SOAPOperationExtractingUtil { 38 | 39 | static Logger log = LoggerFactory.getLogger(SOAPOperationExtractingUtil.class); 40 | String targetNamespace; 41 | 42 | /** 43 | * This method will return the extracted SOAP Operation details of the given WSDL definition 44 | * 45 | * @param wsdlDefinition input wsdl definition to extract the SOAP Operations 46 | * @return returns the {@link WSDLInfo} object carrying the info extracted from the WSDL file 47 | * @throws SOAPToRESTException exception will throw if the SOAP Operation detail extraction is not success 48 | */ 49 | public WSDLInfo getWsdlInfo(Definition wsdlDefinition) throws SOAPToRESTException { 50 | 51 | WSDLInfo wsdlInfo = new WSDLInfo(); 52 | if (wsdlDefinition != null) { 53 | Set soapOperations = getSoapBindingOperations(wsdlDefinition); 54 | //the current implementation only supports WSDL 1.1 version 55 | wsdlInfo.setVersion(SOAPToRESTConstants.WSDL_VERSION_11); 56 | 57 | if (!soapOperations.isEmpty()) { 58 | wsdlInfo.setSoapBindingOperations(soapOperations); 59 | } 60 | wsdlInfo.setHasSoapBindingOperations(hasSoapBindingOperations(wsdlDefinition)); 61 | wsdlInfo.setHasSoap12BindingOperations(hasSoap12BindingOperations(wsdlDefinition)); 62 | 63 | //only support for single service and a port per WSDL file, hence getting the first one 64 | Service service = (Service) wsdlDefinition.getServices().values().iterator().next(); 65 | Port port = (Port) service.getPorts().values().iterator().next(); 66 | wsdlInfo.setSoapService(service.getQName().getLocalPart()); 67 | wsdlInfo.setSoapPort(port.getName()); 68 | 69 | } else { 70 | throw new SOAPToRESTException("WSDL Definition is not initialized."); 71 | } 72 | return wsdlInfo; 73 | } 74 | 75 | /** 76 | * Retrieves all the operations defined in the provided WSDL definition. 77 | * 78 | * @param definition WSDL Definition 79 | * @return a set of {@link WSDLSOAPOperation} defined in the provided WSDL definition 80 | */ 81 | private Set getSoapBindingOperations(Definition definition) throws SOAPToRESTException { 82 | 83 | targetNamespace = definition.getTargetNamespace(); 84 | Set allOperations = new HashSet<>(); 85 | for (Object bindingObj : definition.getAllBindings().values()) { 86 | if (bindingObj instanceof Binding) { 87 | Binding binding = (Binding) bindingObj; 88 | Set operations = getSOAPBindingOperations(binding); 89 | allOperations.addAll(operations); 90 | } 91 | } 92 | return allOperations; 93 | } 94 | 95 | /** 96 | * Retrieves all the operations defined in the provided Binding. 97 | * 98 | * @param binding WSDL binding 99 | * @return a set of {@link WSDLSOAPOperation} defined in the provided Binding 100 | */ 101 | private Set getSOAPBindingOperations(Binding binding) throws SOAPToRESTException { 102 | 103 | Set allBindingOperations = new HashSet<>(); 104 | if (binding.getExtensibilityElements() != null && binding.getExtensibilityElements().size() > 0) { 105 | List extensibilityElements = binding.getExtensibilityElements(); 106 | for (Object extensibilityElement : extensibilityElements) { 107 | if (extensibilityElement instanceof SOAPBinding || extensibilityElement instanceof SOAP12Binding) { 108 | for (Object opObj : binding.getBindingOperations()) { 109 | BindingOperation bindingOperation = (BindingOperation) opObj; 110 | WSDLSOAPOperation wsdlSoapOperation = getSOAPOperation(bindingOperation); 111 | if (wsdlSoapOperation != null) { 112 | allBindingOperations.add(wsdlSoapOperation); 113 | } else { 114 | log.warn("Unable to get soap operation details: " + bindingOperation.getName()); 115 | } 116 | } 117 | } 118 | } 119 | } else { 120 | throw new SOAPToRESTException("Cannot further process to get soap binding operations"); 121 | } 122 | return allBindingOperations; 123 | } 124 | 125 | /** 126 | * Retrieves WSDL operation given the soap binding operation 127 | * 128 | * @param bindingOperation {@link BindingOperation} object 129 | * @return a set of {@link WSDLSOAPOperation} defined in the provided Binding 130 | */ 131 | private WSDLSOAPOperation getSOAPOperation(BindingOperation bindingOperation) { 132 | 133 | WSDLSOAPOperation wsdlOperation = null; 134 | if (bindingOperation.getExtensibilityElements().isEmpty() && bindingOperation.getOperation() != null) { 135 | Operation soapOperation = bindingOperation.getOperation(); 136 | wsdlOperation = new WSDLSOAPOperation(); 137 | wsdlOperation.setName(bindingOperation.getName()); 138 | wsdlOperation.setTargetNamespace(getTargetNamespace(bindingOperation)); 139 | wsdlOperation.setStyle(String.valueOf(soapOperation.getStyle())); 140 | wsdlOperation.setInputParameterModel(getSoapInputParameterModel(bindingOperation)); 141 | wsdlOperation.setOutputParameterModel(getSoapOutputParameterModel(bindingOperation)); 142 | wsdlOperation.setMessageType(getSoapMessageType(bindingOperation)); 143 | 144 | } else { 145 | for (Object boExtElement : bindingOperation.getExtensibilityElements()) { 146 | if (boExtElement instanceof SOAPOperation) { 147 | SOAPOperation soapOperation = (SOAPOperation) boExtElement; 148 | wsdlOperation = new WSDLSOAPOperation(); 149 | wsdlOperation.setName(bindingOperation.getName()); 150 | wsdlOperation.setSoapAction(soapOperation.getSoapActionURI()); 151 | wsdlOperation.setTargetNamespace(getTargetNamespace(bindingOperation)); 152 | wsdlOperation.setStyle(soapOperation.getStyle()); 153 | wsdlOperation.setInputParameterModel(getSoapInputParameterModel(bindingOperation)); 154 | wsdlOperation.setOutputParameterModel(getSoapOutputParameterModel(bindingOperation)); 155 | wsdlOperation.setMessageType(getSoapMessageType(bindingOperation)); 156 | } else if (boExtElement instanceof SOAP12Operation) { 157 | SOAP12Operation soapOperation = (SOAP12Operation) boExtElement; 158 | wsdlOperation = new WSDLSOAPOperation(); 159 | wsdlOperation.setName(bindingOperation.getName()); 160 | wsdlOperation.setSoapAction(soapOperation.getSoapActionURI()); 161 | wsdlOperation.setTargetNamespace(getTargetNamespace(bindingOperation)); 162 | wsdlOperation.setStyle(soapOperation.getStyle()); 163 | wsdlOperation.setInputParameterModel(getSoapInputParameterModel(bindingOperation)); 164 | wsdlOperation.setOutputParameterModel(getSoapOutputParameterModel(bindingOperation)); 165 | wsdlOperation.setMessageType(getSoapMessageType(bindingOperation)); 166 | } 167 | } 168 | } 169 | return wsdlOperation; 170 | } 171 | 172 | /** 173 | * Gets the target namespace given the soap binding operation 174 | * 175 | * @param bindingOperation soap operation 176 | * @return target name space 177 | */ 178 | private String getTargetNamespace(BindingOperation bindingOperation) { 179 | 180 | Operation operation = bindingOperation.getOperation(); 181 | if (operation != null) { 182 | Input input = operation.getInput(); 183 | 184 | if (input != null) { 185 | Message message = input.getMessage(); 186 | if (message != null) { 187 | Map partMap = message.getParts(); 188 | 189 | for (Map.Entry obj : partMap.entrySet()) { 190 | Part part = (Part) obj.getValue(); 191 | if (part != null) { 192 | if (part.getElementName() != null) { 193 | return part.getElementName().getNamespaceURI(); 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | return targetNamespace; 201 | } 202 | 203 | /** 204 | * Gets swagger input parameter model for a given soap operation 205 | * 206 | * @param bindingOperation soap operation 207 | * @return list of swagger models for the parameters 208 | */ 209 | private List getSoapInputParameterModel(BindingOperation bindingOperation) { 210 | 211 | List inputParameterModelList = new ArrayList<>(); 212 | Operation operation = bindingOperation.getOperation(); 213 | if (operation != null) { 214 | Input input = operation.getInput(); 215 | 216 | if (input != null) { 217 | Message message = input.getMessage(); 218 | if (message != null) { 219 | Map map = message.getParts(); 220 | 221 | for (Map.Entry obj : map.entrySet()) { 222 | Part part = (Part) obj.getValue(); 223 | if (part != null) { 224 | if (part.getElementName() != null) { 225 | inputParameterModelList.add(new WSDLParameter(part.getElementName(), 226 | WSDLParameter.MessageType.ELEMENT)); 227 | } else { 228 | if (part.getTypeName() != null) { 229 | inputParameterModelList.add(new WSDLParameter(part.getTypeName(), 230 | WSDLParameter.MessageType.TYPE)); 231 | } 232 | } 233 | } 234 | } 235 | } 236 | } 237 | } 238 | return inputParameterModelList; 239 | } 240 | 241 | /** 242 | * Gets swagger output parameter model for a given soap operation 243 | * 244 | * @param bindingOperation soap operation 245 | * @return list of swagger models for the parameters 246 | */ 247 | private List getSoapOutputParameterModel(BindingOperation bindingOperation) { 248 | 249 | List outputParameterModelList = new ArrayList<>(); 250 | Operation operation = bindingOperation.getOperation(); 251 | if (operation != null) { 252 | Output output = operation.getOutput(); 253 | if (output != null) { 254 | Message message = output.getMessage(); 255 | if (message != null) { 256 | Map map = message.getParts(); 257 | 258 | for (Map.Entry obj : map.entrySet()) { 259 | Part part = (Part) obj.getValue(); 260 | if (part != null) { 261 | if (part.getElementName() != null) { 262 | outputParameterModelList.add(new WSDLParameter(part.getElementName(), 263 | WSDLParameter.MessageType.ELEMENT)); 264 | } else { 265 | if (part.getTypeName() != null) { 266 | outputParameterModelList.add(new WSDLParameter(part.getTypeName(), 267 | WSDLParameter.MessageType.TYPE)); 268 | } 269 | } 270 | } 271 | } 272 | } 273 | } 274 | } 275 | return outputParameterModelList; 276 | } 277 | 278 | /** 279 | * Gets message type for a given soap operation 280 | * 281 | * @param bindingOperation soap operation 282 | * @return String for message type 283 | */ 284 | private String getSoapMessageType(BindingOperation bindingOperation) { 285 | 286 | Operation operation = bindingOperation.getOperation(); 287 | String messageType = ""; 288 | boolean hasRPCMessages = false; 289 | if (operation != null) { 290 | Input input = operation.getInput(); 291 | 292 | if (input != null) { 293 | Message message = input.getMessage(); 294 | if (message != null) { 295 | Map map = message.getParts(); 296 | 297 | for (Map.Entry obj : map.entrySet()) { 298 | Part part = (Part) obj.getValue(); 299 | if (part != null) { 300 | if (part.getElementName() != null) { 301 | messageType = "document"; 302 | } else if (part.getTypeName() != null) { 303 | messageType = "rpc"; 304 | hasRPCMessages = true; 305 | } 306 | } 307 | } 308 | } 309 | } 310 | } 311 | if (hasRPCMessages) { 312 | return "rpc"; 313 | } else { 314 | return messageType; 315 | } 316 | } 317 | 318 | /** 319 | * Returns if the provided WSDL definition contains SOAP binding operations 320 | * 321 | * @return whether the provided WSDL definition contains SOAP binding operations 322 | */ 323 | private boolean hasSoapBindingOperations(Definition wsdlDefinition) { 324 | 325 | if (wsdlDefinition == null) { 326 | return false; 327 | } 328 | for (Object bindingObj : wsdlDefinition.getAllBindings().values()) { 329 | if (bindingObj instanceof Binding) { 330 | Binding binding = (Binding) bindingObj; 331 | for (Object ex : binding.getExtensibilityElements()) { 332 | if (ex instanceof SOAPBinding) { 333 | return true; 334 | } 335 | } 336 | } 337 | } 338 | return false; 339 | } 340 | 341 | /** 342 | * Returns if the provided WSDL definition contains SOAP 1.2 binding operations 343 | * 344 | * @return whether the provided WSDL definition contains SOAP 1.2 binding operations 345 | */ 346 | private boolean hasSoap12BindingOperations(Definition wsdlDefinition) { 347 | 348 | if (wsdlDefinition == null) { 349 | return false; 350 | } 351 | for (Object bindingObj : wsdlDefinition.getAllBindings().values()) { 352 | if (bindingObj instanceof Binding) { 353 | Binding binding = (Binding) bindingObj; 354 | for (Object ex : binding.getExtensibilityElements()) { 355 | if (ex instanceof SOAP12Binding) { 356 | return true; 357 | } 358 | } 359 | } 360 | } 361 | return false; 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/OASGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import io.swagger.v3.oas.models.*; 21 | import io.swagger.v3.oas.models.info.Info; 22 | import io.swagger.v3.oas.models.media.*; 23 | import io.swagger.v3.oas.models.parameters.RequestBody; 24 | import io.swagger.v3.oas.models.responses.ApiResponse; 25 | import io.swagger.v3.oas.models.responses.ApiResponses; 26 | import org.apache.commons.lang3.StringUtils; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | import org.wso2.soaptorest.models.*; 30 | import org.wso2.soaptorest.utils.SOAPToRESTConstants; 31 | 32 | import javax.xml.namespace.QName; 33 | import java.util.Arrays; 34 | import java.util.HashMap; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Set; 38 | 39 | public class OASGenerator { 40 | 41 | static Logger log = LoggerFactory.getLogger(OASGenerator.class); 42 | 43 | /** 44 | * Generate the swagger from the WSDL info 45 | * 46 | * @param wsdlInfo WSDLInfo object which has parsed WSDL data 47 | * @return Generated the swagger from the WSDL info 48 | */ 49 | public static OpenAPI generateOpenAPIFromWSDL(WSDLInfo wsdlInfo, List xsModel, String APITitle, 50 | String APIVersion) { 51 | 52 | Set operations; 53 | operations = wsdlInfo.getSoapBindingOperations(); 54 | populateSoapOperationParameters(operations); 55 | OpenAPI openAPI = new OpenAPI(); 56 | Paths paths = new Paths(); 57 | 58 | for (WSDLSOAPOperation operation : operations) { 59 | PathItem pathItem = new PathItem(); 60 | Operation swaggerOperation = new Operation(); 61 | ObjectSchema inputModelSchema = new ObjectSchema(); 62 | if (operation.getInputParameterModel() != null) { 63 | for (WSDLParameter inputQName : operation.getInputParameterModel()) { 64 | if (inputQName.getMessageType() == WSDLParameter.MessageType.TYPE) { 65 | Schema inputProp = getDataTypesSchema(inputQName.getQName()); 66 | inputModelSchema.addProperties("parameter", inputProp); 67 | } else { 68 | Schema inputRefProp = new Schema<>(); 69 | inputRefProp.setName(inputQName.getQName().getLocalPart().replaceAll("\\s+", "")); 70 | inputRefProp.set$ref(SOAPToRESTConstants.OAS_DEFINITIONS_ROOT_ELEMENT_PATH + inputQName. 71 | getQName().getLocalPart()); 72 | inputModelSchema.addProperties(inputQName.getQName().getLocalPart(), inputRefProp); 73 | } 74 | } 75 | } else { 76 | inputModelSchema.set$ref(SOAPToRESTConstants.OAS_DEFINITIONS_PREFIX + operation.getName()); 77 | } 78 | 79 | RequestBody requestBody = new RequestBody(); 80 | requestBody.setRequired(true); 81 | Content content = new Content(); 82 | MediaType mediaType = new MediaType(); 83 | mediaType.setSchema(inputModelSchema); 84 | content.addMediaType(SOAPToRESTConstants.OAS_ALL_MEDIA_TYPE, mediaType); 85 | requestBody.setContent(content); 86 | swaggerOperation.setRequestBody(requestBody); 87 | 88 | //adding response 89 | ApiResponses responses = new ApiResponses(); 90 | ApiResponse response = new ApiResponse(); 91 | ObjectSchema outputModelSchema = new ObjectSchema(); 92 | if (operation.getOutputParameterModel() != null) { 93 | for (WSDLParameter outputParamQName : operation.getOutputParameterModel()) { 94 | if (outputParamQName.getMessageType() == WSDLParameter.MessageType.TYPE) { 95 | Schema outputProp = getDataTypesSchema(outputParamQName.getQName()); 96 | outputModelSchema.addProperties("parameter", outputProp); 97 | } else { 98 | Schema outputRefProp = new Schema<>(); 99 | outputRefProp.setName(SOAPToRESTConstants.OAS_DEFINITIONS_ROOT_ELEMENT_PATH + outputParamQName.getQName().getLocalPart().replaceAll("\\s+", "")); 100 | outputRefProp.set$ref( 101 | SOAPToRESTConstants.OAS_DEFINITIONS_ROOT_ELEMENT_PATH + outputParamQName.getQName().getLocalPart()); 102 | outputModelSchema.addProperties(outputParamQName.getQName().getLocalPart(), outputRefProp); 103 | } 104 | } 105 | 106 | } else { 107 | outputModelSchema.set$ref(SOAPToRESTConstants.OAS_DEFINITIONS_PREFIX + operation.getName()); 108 | } 109 | Content outputContent = new Content(); 110 | MediaType outputMediaType = new MediaType(); 111 | outputMediaType.setSchema(outputModelSchema); 112 | outputContent.addMediaType(SOAPToRESTConstants.OAS_ALL_MEDIA_TYPE, outputMediaType); 113 | response.setContent(outputContent); 114 | response.setDescription(SOAPToRESTConstants.DEFAULT_DESCRIPTION); 115 | responses.setDefault(response); 116 | swaggerOperation.setResponses(responses); 117 | swaggerOperation.setOperationId(operation.getSoapBindingOpName()); 118 | //setting vendor extensions 119 | Map extensions = new HashMap<>(); 120 | extensions.put(SOAPToRESTConstants.SOAP_ACTION, operation.getSoapAction()); 121 | extensions.put(SOAPToRESTConstants.SOAP_OPERATION, operation.getSoapBindingOpName()); 122 | extensions.put(SOAPToRESTConstants.NAMESPACE, operation.getTargetNamespace()); 123 | if (wsdlInfo.isHasSoap12BindingOperations()) { 124 | extensions.put(SOAPToRESTConstants.SOAP_VERSION, SOAPToRESTConstants.SOAP_VERSION_12); 125 | } else if (wsdlInfo.hasSoapBindingOperations()) { 126 | extensions.put(SOAPToRESTConstants.SOAP_VERSION, SOAPToRESTConstants.SOAP_VERSION_11); 127 | } 128 | extensions.put(SOAPToRESTConstants.SOAP_STYLE, operation.getStyle()); 129 | extensions.put(SOAPToRESTConstants.SOAP_MESSAGE_TYPE, operation.getMessageType()); 130 | Map extensionMap = new HashMap<>(); 131 | extensionMap.put(SOAPToRESTConstants.WSO2_SOAP, extensions); 132 | swaggerOperation.setExtensions(extensionMap); 133 | 134 | if (operation.getHttpVerb().equals(SOAPToRESTConstants.HTTP_METHOD_GET)) { 135 | pathItem.setGet(swaggerOperation); 136 | } else { 137 | pathItem.setPost(swaggerOperation); 138 | } 139 | paths.addPathItem("/" + operation.getName(), pathItem); 140 | } 141 | 142 | openAPI.paths(paths); 143 | Info info = new Info(); 144 | info.setTitle(APITitle != null ? APITitle : SOAPToRESTConstants.EMPTY_STRING); 145 | info.setVersion(APIVersion != null ? APIVersion : SOAPToRESTConstants.EMPTY_STRING); 146 | openAPI.info(info); 147 | openAPI.setComponents(generateOASSchemas(xsModel)); 148 | 149 | return openAPI; 150 | 151 | } 152 | 153 | /** 154 | * gets parameters from the soap operation and populates them in {@link WSDLSOAPOperation} 155 | * 156 | * @param soapOperations soap binding operations 157 | */ 158 | private static void populateSoapOperationParameters(Set soapOperations) { 159 | 160 | if (soapOperations != null) { 161 | for (WSDLSOAPOperation operation : soapOperations) { 162 | String resourcePath; 163 | String operationName = operation.getName(); 164 | operation.setSoapBindingOpName(operationName); 165 | operation.setHttpVerb(SOAPToRESTConstants.HTTP_METHOD_POST); 166 | resourcePath = operationName; 167 | resourcePath = resourcePath.substring(0, 1).toLowerCase() + resourcePath.substring(1); 168 | operation.setName(resourcePath.replaceAll("\\s+", "")); 169 | if (log.isDebugEnabled()) { 170 | log.debug("REST resource path for SOAP operation: " + operationName + " is: " + resourcePath); 171 | } 172 | } 173 | } else { 174 | log.info("No SOAP operations found in the WSDL"); 175 | } 176 | } 177 | 178 | private static Components generateOASSchemas(List xsModelList) { 179 | 180 | Components components = new Components(); 181 | 182 | for (XSModel xsModel : xsModelList) { 183 | List xsDataTypeList = xsModel.getXsDataTypes(); 184 | for (XSDataType xsDataType : xsDataTypeList) { 185 | Schema schema = getSchemaForXSDataType(xsDataType, null, xsModel.isElementFormDefaultQualified()); 186 | components.addSchemas(schema.getName(), schema); 187 | } 188 | List xsDataGroupList = xsModel.getGroups(); 189 | for (XSGroup xsGroup : xsDataGroupList) { 190 | Schema schema = new ObjectSchema(); 191 | schema.setName(xsGroup.getName().getLocalPart()); 192 | processXSGroup(xsGroup, schema, xsModel.isElementFormDefaultQualified()); 193 | components.addSchemas(schema.getName(), schema); 194 | } 195 | 196 | if (xsModel.getElements().size() > 0) { 197 | //Process the elements defined in the root XSD and add 'rootElement_' prefix to identify uniquely 198 | for (XSElement xsElement : xsModel.getElements()) { 199 | Schema schema = getSchemaForXSElement(xsElement, xsModel.isElementFormDefaultQualified()); 200 | schema.setName("rootElement_" + xsElement.getName().getLocalPart().replaceAll("\\s+", "")); 201 | schema.setType("object"); 202 | components.addSchemas(schema.getName(), schema); 203 | 204 | } 205 | 206 | } 207 | } 208 | return components; 209 | } 210 | 211 | private static Schema getSchemaForXSDataType(XSDataType xsDataType, String parentName, boolean isElementFormDefaultQualified) { 212 | String schemaName; 213 | Schema schema; 214 | if (xsDataType.getName() != null) { 215 | schemaName = xsDataType.getName().getLocalPart().replaceAll("\\s+", ""); 216 | } else if (parentName != null) { 217 | schemaName = parentName; 218 | } else { 219 | schemaName = "Default_Object"; 220 | } 221 | if (xsDataType.isSimpleType()) { 222 | schema = new StringSchema(); 223 | schema.setName(schemaName); 224 | schema.setType("string"); 225 | } else { 226 | schema = new ObjectSchema(); 227 | schema.setName(schemaName); 228 | if (xsDataType.getExtensionBase() != null) { 229 | schema.addProperties(SOAPToRESTConstants.EXTENSION_NAME, getDataTypesSchema(xsDataType.getExtensionBase())); 230 | Map extensionStringObjectMap = new HashMap<>(); 231 | XML xml = new XML(); 232 | if (xsDataType.getName() != null && StringUtils.isNotBlank(xsDataType.getName().getNamespaceURI())) { 233 | xml.setNamespace(xsDataType.getName().getNamespaceURI()); 234 | xml.setPrefix(xsDataType.getName().getPrefix()); 235 | } 236 | extensionStringObjectMap.put(SOAPToRESTConstants.X_NAMESPACE_QUALIFIED, isElementFormDefaultQualified); 237 | schema.setXml(xml); 238 | schema.setExtensions(extensionStringObjectMap); 239 | } 240 | if (xsDataType.getSequence() != null) { 241 | processXSSequence(xsDataType.getSequence(), schema, isElementFormDefaultQualified); 242 | Map extensionStringObjectMap = new HashMap<>(); 243 | XML xml = new XML(); 244 | if (xsDataType.getName() != null && StringUtils.isNotBlank(xsDataType.getName().getNamespaceURI())) { 245 | xml.setNamespace(xsDataType.getName().getNamespaceURI()); 246 | xml.setPrefix(xsDataType.getName().getPrefix()); 247 | } 248 | extensionStringObjectMap.put(SOAPToRESTConstants.X_NAMESPACE_QUALIFIED, isElementFormDefaultQualified); 249 | schema.setXml(xml); 250 | schema.setExtensions(extensionStringObjectMap); 251 | } 252 | if (xsDataType.getChoice() != null) { 253 | processXSChoice(xsDataType.getChoice(), schema, isElementFormDefaultQualified); 254 | } 255 | if (xsDataType.getGroup() != null) { 256 | processXSGroup(xsDataType.getGroup(), schema, isElementFormDefaultQualified); 257 | } 258 | } 259 | return schema; 260 | } 261 | 262 | private static void processXSSequence(XSSequence xsSequence, Schema parentSchema, boolean isElementFormDefaultQualified) { 263 | 264 | if (xsSequence.getElementList() != null) { 265 | List xsElementList = xsSequence.getElementList(); 266 | for (XSElement xsElement : xsElementList) { 267 | Schema innerSchema = getSchemaForXSElement(xsElement, isElementFormDefaultQualified); 268 | if (innerSchema != null) { 269 | parentSchema.addProperties(innerSchema.getName(), innerSchema); 270 | // if element is not optional, add it to the required list of parent schema 271 | if (!xsElement.isOptional() && xsElement.getName() != null) { 272 | if (parentSchema.getRequired() != null) { 273 | parentSchema.getRequired().add(xsElement.getName().getLocalPart()); 274 | } else { 275 | parentSchema.setRequired(Arrays.asList(xsElement.getName().getLocalPart())); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | if (xsSequence.getSequenceList() != null) { 282 | for (XSSequence innerXSequence : xsSequence.getSequenceList()) { 283 | processXSSequence(innerXSequence, parentSchema, isElementFormDefaultQualified); 284 | } 285 | } 286 | if (xsSequence.getChoiceList() != null) { 287 | List xsChoiceList = xsSequence.getChoiceList(); 288 | for (XSChoice xsChoice : xsChoiceList) { 289 | processXSChoice(xsChoice, parentSchema, isElementFormDefaultQualified); 290 | } 291 | } 292 | } 293 | 294 | private static void processXSChoice(XSChoice xsChoice, Schema parentSchema, boolean isElementFormDefaultQualified) { 295 | 296 | if (xsChoice.getSequenceList() != null) { 297 | for (XSSequence xsSequence : xsChoice.getSequenceList()) { 298 | processXSSequence(xsSequence, parentSchema, isElementFormDefaultQualified); 299 | } 300 | } 301 | if (xsChoice.getChoiceList() != null) { 302 | for (XSChoice innerXsChoice : xsChoice.getChoiceList()) { 303 | processXSChoice(innerXsChoice, parentSchema, isElementFormDefaultQualified); 304 | } 305 | } 306 | if (xsChoice.getGroupsList() != null) { 307 | for (XSGroup xsGroup : xsChoice.getGroupsList()) { 308 | processXSGroup(xsGroup, parentSchema, isElementFormDefaultQualified); 309 | } 310 | } 311 | if (xsChoice.getElementList() != null) { 312 | for (XSElement xsElement : xsChoice.getElementList()) { 313 | Schema innerSchema = getSchemaForXSElement(xsElement, isElementFormDefaultQualified); 314 | parentSchema.addProperties(innerSchema.getName(), innerSchema); 315 | } 316 | } 317 | } 318 | 319 | private static void processXSGroup(XSGroup xsGroup, Schema parentSchema, boolean isElementFormDefaultQualified) { 320 | 321 | if (xsGroup.getChoiceList() != null) { 322 | for (XSChoice xsChoice : xsGroup.getChoiceList()) { 323 | processXSChoice(xsChoice, parentSchema, isElementFormDefaultQualified); 324 | } 325 | } 326 | if (xsGroup.getSequenceList() != null) { 327 | for (XSSequence xsSequence : xsGroup.getSequenceList()) { 328 | processXSSequence(xsSequence, parentSchema, isElementFormDefaultQualified); 329 | } 330 | } 331 | if (xsGroup.getRefKey() != null) { 332 | parentSchema.set$ref(SOAPToRESTConstants.OAS_DEFINITIONS_PREFIX + xsGroup.getRefKey().getLocalPart()); 333 | } 334 | } 335 | 336 | private static Schema getSchemaForXSElement(XSElement xsElement, boolean isElementFormDefaultQualified) { 337 | 338 | Schema schema = null; 339 | if (xsElement.getType() != null) { 340 | if (xsElement.isArray()) { 341 | ArraySchema arraySchema = new ArraySchema(); 342 | arraySchema.setItems(getDataTypesSchema(xsElement.getType())); 343 | schema = arraySchema; 344 | } else { 345 | schema = getDataTypesSchema(xsElement.getType()); 346 | } 347 | if (xsElement.getName() != null) { 348 | schema.setName(xsElement.getName().getLocalPart().replaceAll("\\s+", "")); 349 | } 350 | } else if (xsElement.getRefKey() != null) { 351 | schema = new Schema<>(); 352 | schema.setName(xsElement.getRefKey().getLocalPart().replaceAll("\\s+", "")); 353 | schema.$ref(SOAPToRESTConstants.OAS_DEFINITIONS_PREFIX + xsElement.getRefKey().getLocalPart()); 354 | } else if (xsElement.getInlineComplexType() != null) { 355 | schema = getSchemaForXSDataType(xsElement.getInlineComplexType(), 356 | xsElement.getName().getLocalPart().replaceAll("\\s+", ""), isElementFormDefaultQualified); 357 | } 358 | return schema; 359 | } 360 | 361 | public static Schema getDataTypesSchema(QName type) { 362 | 363 | Schema outputSchema; 364 | String dataType = type.getLocalPart(); 365 | switch (dataType) { 366 | case "string": 367 | case "anyType": 368 | case "duration": 369 | case "time": 370 | case "gYearMonth": 371 | case "gMonthDay": 372 | case "gYear": 373 | case "gDay": 374 | case "gMonth": 375 | case "anyURI": 376 | case "QName": 377 | case "normalizedString": 378 | case "token": 379 | outputSchema = new StringSchema(); 380 | outputSchema.setType("string"); 381 | break; 382 | case "dateTime": 383 | outputSchema = new DateTimeSchema(); 384 | outputSchema.setType("string"); 385 | outputSchema.setFormat("date-time"); 386 | break; 387 | case "date": 388 | outputSchema = new DateSchema(); 389 | outputSchema.setType("string"); 390 | outputSchema.setFormat("date"); 391 | break; 392 | case "hexBinary": 393 | outputSchema = new StringSchema(); 394 | outputSchema.setType("string"); 395 | outputSchema.setFormat("binary"); 396 | break; 397 | case "base64Binary": 398 | case "unsignedByte": 399 | outputSchema = new StringSchema(); 400 | outputSchema.setType("string"); 401 | outputSchema.setFormat("byte"); 402 | break; 403 | case "integer": 404 | case "nonPositiveInteger": 405 | case "negativeInteger": 406 | case "positiveInteger": 407 | case "nonNegativeInteger": 408 | case "unsignedInt": 409 | case "int": 410 | case "short": 411 | case "unsignedShort": 412 | outputSchema = new IntegerSchema(); 413 | outputSchema.setType("integer"); 414 | outputSchema.setFormat("int32"); 415 | break; 416 | case "long": 417 | case "unsignedLong": 418 | outputSchema = new IntegerSchema(); 419 | outputSchema.setType("integer"); 420 | outputSchema.setFormat("int64"); 421 | break; 422 | case "boolean": 423 | outputSchema = new BooleanSchema(); 424 | outputSchema.setType("boolean"); 425 | break; 426 | case "decimal": 427 | case "float": 428 | outputSchema = new NumberSchema(); 429 | outputSchema.setType("number"); 430 | outputSchema.setFormat("float"); 431 | break; 432 | case "double": 433 | outputSchema = new NumberSchema(); 434 | outputSchema.setType("number"); 435 | outputSchema.setFormat("double"); 436 | break; 437 | default: 438 | outputSchema = new Schema<>(); 439 | outputSchema.$ref(SOAPToRESTConstants.OAS_DEFINITIONS_PREFIX + type.getLocalPart()); 440 | } 441 | return outputSchema; 442 | } 443 | 444 | } 445 | -------------------------------------------------------------------------------- /src/main/java/org/wso2/soaptorest/SOAPRequestBodyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. 3 | * 4 | * WSO2 Inc. licenses this file to you under the Apache License, 5 | * Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, 12 | * software distributed under the License is distributed on an 13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | * KIND, either express or implied. See the License for the 15 | * specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | package org.wso2.soaptorest; 19 | 20 | import com.fasterxml.jackson.databind.module.SimpleModule; 21 | import io.swagger.oas.inflector.examples.ExampleBuilder; 22 | import io.swagger.oas.inflector.examples.models.Example; 23 | import io.swagger.oas.inflector.processors.JsonNodeExampleSerializer; 24 | import io.swagger.util.Json; 25 | import io.swagger.v3.core.util.Yaml; 26 | import io.swagger.v3.oas.models.OpenAPI; 27 | import io.swagger.v3.oas.models.Operation; 28 | import io.swagger.v3.oas.models.PathItem; 29 | import io.swagger.v3.oas.models.Paths; 30 | import io.swagger.v3.oas.models.media.Schema; 31 | import io.swagger.v3.oas.models.parameters.Parameter; 32 | import io.swagger.v3.oas.models.parameters.QueryParameter; 33 | import org.apache.commons.lang3.StringUtils; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | import org.w3c.dom.Document; 37 | import org.w3c.dom.Element; 38 | import org.w3c.dom.Node; 39 | import org.w3c.dom.NodeList; 40 | import org.wso2.soaptorest.exceptions.SOAPToRESTException; 41 | import org.wso2.soaptorest.models.SOAPRequestElement; 42 | import org.wso2.soaptorest.models.SOAPtoRESTConversionData; 43 | import org.wso2.soaptorest.utils.ListJSONPaths; 44 | import org.wso2.soaptorest.utils.SOAPToRESTConstants; 45 | 46 | import javax.xml.parsers.DocumentBuilder; 47 | import javax.xml.parsers.DocumentBuilderFactory; 48 | import javax.xml.parsers.ParserConfigurationException; 49 | import java.io.StringWriter; 50 | import java.util.ArrayList; 51 | import java.util.HashMap; 52 | import java.util.List; 53 | import java.util.Map; 54 | 55 | import static org.wso2.soaptorest.utils.SOAPToRESTConstants.ATTRIBUTE_PLACEHOLDER; 56 | import static org.wso2.soaptorest.utils.SOAPToRESTConstants.IF_PLACEHOLDER; 57 | import static org.wso2.soaptorest.utils.SOAPToRESTConstants.IS_EMPTY_ATTRIBUTE; 58 | import static org.wso2.soaptorest.utils.SOAPToRESTConstants.QUESTION_MARK_PLACEHOLDER; 59 | import static org.wso2.soaptorest.utils.SOAPToRESTConstants.VALUE_ATTRIBUTE; 60 | 61 | /** 62 | * Class that reads OpenAPI and generate soap request payloads 63 | */ 64 | public class SOAPRequestBodyGenerator { 65 | 66 | private static final Logger log = LoggerFactory.getLogger(SOAPRequestBodyGenerator.class); 67 | private static String soapMessageType = SOAPToRESTConstants.EMPTY_STRING; 68 | private static String soapStyle = SOAPToRESTConstants.EMPTY_STRING; 69 | 70 | /** 71 | * Generates {@link SOAPtoRESTConversionData} with a map of SOAP payloads with JSON paths of REST payloads for 72 | * all generated REST endpoint. Mapping is done using Operation ID of REST service. 73 | * 74 | * @param openAPI open api definition of api 75 | * @return SOAPtoRESTConversionData Object that represent the OpenAPI with SOAP payloads which are needed 76 | * for SOAP backend calls 77 | * @throws SOAPToRESTException throws {@link SOAPToRESTException} if exception occur while generating SOAP 78 | * payloads 79 | */ 80 | public static SOAPtoRESTConversionData generateSOAPtoRESTConversionObjectFromOAS(OpenAPI openAPI, 81 | String soapService, 82 | String soapPort) throws 83 | SOAPToRESTException { 84 | 85 | Map requestBodies = new HashMap<>(); 86 | Paths paths = openAPI.getPaths(); 87 | 88 | // Configure serializers 89 | SimpleModule simpleModule = new SimpleModule().addSerializer(new JsonNodeExampleSerializer()); 90 | Json.mapper().registerModule(simpleModule); 91 | Yaml.mapper().registerModule(simpleModule); 92 | Map jsonPathAndSchemaMap = new HashMap<>(); 93 | 94 | for (String pathName : paths.keySet()) { 95 | PathItem path = paths.get(pathName); 96 | List operationMap = path.readOperations(); 97 | for (Operation operation : operationMap) { 98 | ArrayList parameterJsonPathMapping = new ArrayList<>(); 99 | Map queryParameters = new HashMap<>(); 100 | String operationId = operation.getOperationId(); 101 | 102 | //get vendor extensions 103 | Map vendorExtensions = operation.getExtensions(); 104 | Object vendorExtensionObj = vendorExtensions.get(SOAPToRESTConstants.WSO2_SOAP); 105 | 106 | String soapAction = SOAPToRESTConstants.EMPTY_STRING; 107 | String namespace = SOAPToRESTConstants.EMPTY_STRING; 108 | String soapVersion = SOAPToRESTConstants.EMPTY_STRING; 109 | if (vendorExtensionObj != null) { 110 | soapAction = 111 | (String) ((HashMap) vendorExtensionObj).get(SOAPToRESTConstants.SOAP_ACTION); 112 | namespace = 113 | (String) ((HashMap) vendorExtensionObj).get(SOAPToRESTConstants.NAMESPACE); 114 | soapVersion = 115 | (String) ((HashMap) vendorExtensionObj).get(SOAPToRESTConstants.SOAP_VERSION); 116 | soapMessageType = 117 | (String) ((HashMap) vendorExtensionObj).get(SOAPToRESTConstants.SOAP_MESSAGE_TYPE); 118 | soapStyle = 119 | (String) ((HashMap) vendorExtensionObj).get(SOAPToRESTConstants.SOAP_STYLE); 120 | } 121 | String soapNamespace = SOAPToRESTConstants.SOAP12_NAMESPACE; 122 | if (StringUtils.isNotBlank(soapVersion) && SOAPToRESTConstants.SOAP_VERSION_11.equals(soapVersion)) { 123 | soapNamespace = SOAPToRESTConstants.SOAP11_NAMESPACE; 124 | } 125 | 126 | List parameters = operation.getParameters(); 127 | 128 | if (parameters != null) { 129 | for (Parameter parameter : parameters) { 130 | String name = parameter.getName(); 131 | 132 | if (parameter instanceof QueryParameter) { 133 | String type = parameter.getSchema().getType(); 134 | queryParameters.put(name, type); 135 | } 136 | } 137 | } else { 138 | try { 139 | Schema model = 140 | operation.getRequestBody().getContent().get(SOAPToRESTConstants. 141 | DEFAULT_CONTENT_TYPE).getSchema(); 142 | Example example = ExampleBuilder.fromSchema(model, openAPI.getComponents().getSchemas()); 143 | parameterJsonPathMapping = ListJSONPaths.getJsonPathsFromExample(example, jsonPathAndSchemaMap); 144 | } catch (Exception e) { 145 | throw new SOAPToRESTException("Cannot generate JSON body from the OpenAPI", e); 146 | } 147 | 148 | } 149 | 150 | Document soapRequestBody = createSOAPRequestXMLForOperation(parameterJsonPathMapping, queryParameters, 151 | namespace, operationId, openAPI, jsonPathAndSchemaMap ); 152 | 153 | iterateChildNodes(soapRequestBody.getDocumentElement(), soapRequestBody); 154 | requestBodies.put(operationId, new SOAPRequestElement(soapRequestBody, soapAction, namespace, 155 | soapNamespace)); 156 | } 157 | } 158 | return new SOAPtoRESTConversionData(openAPI, requestBodies, soapService, soapPort); 159 | } 160 | 161 | /** 162 | * Iterate through the given document and wrap the possible empty elements with <#if> statements. 163 | * @param node Current node 164 | * @param document Root document 165 | */ 166 | private static void iterateChildNodes(Node node, Document document) { 167 | // Get the child nodes of the current node 168 | NodeList childNodes = node.getChildNodes(); 169 | 170 | // Iterate over the child nodes 171 | for (int i = 0; i < childNodes.getLength(); i++) { 172 | Node childNode = childNodes.item(i); 173 | 174 | // Check if the element has the attribute "addIsEmptyCheck" with value "true" 175 | if (childNode instanceof Element) { 176 | Element element = (Element) childNode; 177 | if (element.hasAttribute(IS_EMPTY_ATTRIBUTE) && 178 | element.getAttribute(IS_EMPTY_ATTRIBUTE).equals("true")) { 179 | // Create a new element <#if> 180 | String value = null; 181 | if (element.hasAttribute(VALUE_ATTRIBUTE)) { 182 | String nodeName = element.getNodeName(); 183 | // remove namespace from the node name 184 | if (nodeName.contains(":")) { 185 | nodeName = nodeName.split(":")[1]; 186 | } 187 | value = "${payload." + element.getAttribute(VALUE_ATTRIBUTE) + "." + nodeName + "}"; 188 | element.removeAttribute(VALUE_ATTRIBUTE); 189 | } 190 | element.removeAttribute(IS_EMPTY_ATTRIBUTE); 191 | 192 | Element newElement = document.createElement(IF_PLACEHOLDER); 193 | // remove ${} from the value and append has_content check 194 | newElement.setAttribute(ATTRIBUTE_PLACEHOLDER, value.substring(2, value.length() - 1) + 195 | QUESTION_MARK_PLACEHOLDER + "has_content"); 196 | 197 | Node parentNode = element.getParentNode(); 198 | parentNode.replaceChild(newElement, element); 199 | newElement.appendChild(element); 200 | 201 | iterateChildNodes(newElement, document); 202 | } else { 203 | iterateChildNodes(element, document); 204 | } 205 | } else { 206 | iterateChildNodes(childNode, document); 207 | } 208 | } 209 | } 210 | private static Document createSOAPRequestXMLForOperation(ArrayList parameterJsonPathMapping, Map queryPathParamMapping, String namespace, String operationId, OpenAPI openAPI, 212 | Map jsonPathAndSchemaMap) throws SOAPToRESTException { 213 | 214 | DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); 215 | DocumentBuilder docBuilder; 216 | StringWriter stringWriter = new StringWriter(); 217 | boolean isNamespaceQualified = false; 218 | boolean isRootComplexType = false; 219 | Document doc; 220 | try { 221 | docBuilder = docFactory.newDocumentBuilder(); 222 | doc = docBuilder.newDocument(); 223 | Element rootElement = null; 224 | if (SOAPToRESTConstants.SOAP_RPC_MESSAGE_TYPE.equalsIgnoreCase(soapMessageType) || 225 | SOAPToRESTConstants.SOAP_RPC_MESSAGE_TYPE.equalsIgnoreCase(soapStyle) || 226 | parameterJsonPathMapping.isEmpty()) { 227 | rootElement = doc.createElementNS(namespace, 228 | SOAPToRESTConstants.NAMESPACE_PREFIX + 229 | SOAPToRESTConstants.NAMESPACE_SEPARATOR + operationId); 230 | doc.appendChild(rootElement); 231 | } 232 | for (String parameter : parameterJsonPathMapping) { 233 | String[] parameterTreeNodes = parameter.split("\\."); 234 | 235 | Element prevElement = rootElement; 236 | int elemPos = 0; 237 | int length = parameterTreeNodes.length; 238 | if (length > 0 && !isRootComplexType) { 239 | isRootComplexType = true; 240 | } 241 | String currentJSONPath = "payload"; 242 | String notEscapedJSONPath = "payload"; 243 | for (int i = 0; i < length; i++) { 244 | String parameterTreeNode = parameterTreeNodes[i]; 245 | boolean isArray = false; 246 | currentJSONPath = currentJSONPath.isEmpty() ? escapeFreeMarkerTemplate(parameterTreeNode) : 247 | currentJSONPath + "." + escapeFreeMarkerTemplate(parameterTreeNode); 248 | notEscapedJSONPath = notEscapedJSONPath.isEmpty() ? parameterTreeNode : 249 | notEscapedJSONPath + "." + parameterTreeNode; 250 | if (parameterTreeNode.endsWith("[0]")) { 251 | isArray = true; 252 | parameterTreeNode = parameterTreeNode.replace("[0]", ""); 253 | } 254 | Schema schema = openAPI.getComponents().getSchemas().get(parameterTreeNode); 255 | // Since we add the elements defined in the root XSD with the 'rootElement_' prefix, we need to 256 | // check for the schema with the prefix if the schema is not found. 257 | if (schema == null) { 258 | schema = openAPI.getComponents().getSchemas() 259 | .get(SOAPToRESTConstants.ROOT_ELEMENT_PREFIX + parameterTreeNode); 260 | } 261 | if (schema != null) { 262 | Map vendorExtensions = schema.getExtensions(); 263 | if (vendorExtensions != null && vendorExtensions.get(SOAPToRESTConstants.X_NAMESPACE_QUALIFIED) != null 264 | && Boolean.parseBoolean(vendorExtensions.get(SOAPToRESTConstants.X_NAMESPACE_QUALIFIED). 265 | toString())) { 266 | isNamespaceQualified = true; 267 | } 268 | } 269 | Schema parentSchema = null; 270 | boolean needIsEmptyCheck = false; 271 | // Check parent schema for required fields and wrap with isEmpty check if required 272 | if (prevElement != null) { 273 | String mapKey = parameterTreeNode; 274 | // payload. is 8 characters long 275 | if (notEscapedJSONPath.length() > 8 + parameterTreeNode.length()) { 276 | mapKey = notEscapedJSONPath.substring(8, notEscapedJSONPath.length() 277 | - parameterTreeNode.length() - 1); 278 | } 279 | parentSchema = openAPI.getComponents().getSchemas().get(jsonPathAndSchemaMap.get(mapKey)); 280 | if (parentSchema == null) { 281 | // check for the schema inside parent object's schema 282 | String parentKey = mapKey.substring(0, mapKey.lastIndexOf('.')); 283 | if (!StringUtils.isEmpty(parentKey)) { 284 | Schema enclosingSchema = openAPI.getComponents().getSchemas() 285 | .get(jsonPathAndSchemaMap.get(parentKey)); 286 | if (enclosingSchema != null && enclosingSchema.getProperties() != null && 287 | enclosingSchema.getProperties().containsKey(prevElement.getLocalName())) { 288 | parentSchema = enclosingSchema.getProperties().get(prevElement.getLocalName()); 289 | } 290 | } 291 | } 292 | if (parentSchema != null && (parentSchema.getRequired() == null || 293 | !parentSchema.getRequired().contains(parameterTreeNode))) { 294 | needIsEmptyCheck = true; 295 | } 296 | } 297 | 298 | String payloadPrefix = "${"; 299 | if (StringUtils.isNotBlank(parameterTreeNode)) { 300 | if (SOAPToRESTConstants.ATTR_CONTENT_KEYWORD.equalsIgnoreCase(parameterTreeNode)) { 301 | String attName = parameterTreeNodes[++i]; 302 | prevElement.setAttribute(attName, payloadPrefix + currentJSONPath + "}"); 303 | break; 304 | } 305 | if (SOAPToRESTConstants.BASE_CONTENT_KEYWORD.equalsIgnoreCase(parameterTreeNode)) { 306 | prevElement.setTextContent(payloadPrefix + currentJSONPath + "}"); 307 | break; 308 | } 309 | Element element; 310 | 311 | if (isNamespaceQualified) { 312 | element = doc.createElementNS(namespace, 313 | SOAPToRESTConstants.NAMESPACE_PREFIX + 314 | SOAPToRESTConstants.NAMESPACE_SEPARATOR + parameterTreeNode); 315 | } else if (isRootComplexType) { 316 | element = doc.createElementNS(namespace, 317 | SOAPToRESTConstants.NAMESPACE_PREFIX + 318 | SOAPToRESTConstants.NAMESPACE_SEPARATOR + parameterTreeNode); 319 | isRootComplexType = false; 320 | } else { 321 | element = doc.createElement( parameterTreeNode); 322 | } 323 | if (isArray) { 324 | element.setAttribute(SOAPToRESTConstants.ARRAY_PLACEHOLDER, 325 | currentJSONPath.replace("[0]", "")); 326 | currentJSONPath = escapeFreeMarkerTemplate(parameterTreeNode); 327 | } 328 | 329 | String xPathOfNode = StringUtils.EMPTY; 330 | if (doc.getElementsByTagName(element.getTagName()).getLength() > 0) { 331 | xPathOfNode = getXpath(doc.getElementsByTagName(element.getTagName()).item(0)); 332 | xPathOfNode = xPathOfNode.replaceAll("/+", "."); 333 | if (xPathOfNode.startsWith(".")) { 334 | xPathOfNode = xPathOfNode.substring(1); 335 | } 336 | if (xPathOfNode.contains(operationId + ".")) { 337 | xPathOfNode = xPathOfNode.replace(operationId + ".", ""); 338 | } 339 | } 340 | 341 | if (doc.getElementsByTagName(element.getTagName()).getLength() > 0 && 342 | parameter.contains(xPathOfNode) && 343 | rootElement != doc.getElementsByTagName(element.getTagName()).item(0)) { 344 | prevElement = (Element) doc.getElementsByTagName(element.getTagName()).item(0); 345 | } else { 346 | if (elemPos == length - 1) { 347 | element.setTextContent(payloadPrefix + currentJSONPath + "}"); 348 | } 349 | if (needIsEmptyCheck) { 350 | String path = ""; 351 | for (int j = 0; j < i; j++) { 352 | path = path.concat(escapeFreeMarkerTemplate(parameterTreeNodes[j])).concat("."); 353 | } 354 | element.setAttribute(IS_EMPTY_ATTRIBUTE, "true"); 355 | if (path.length() == 0) { 356 | element.setAttribute(VALUE_ATTRIBUTE, ""); 357 | } else { 358 | element.setAttribute(VALUE_ATTRIBUTE, path.substring(0, path.length() - 1)); 359 | } 360 | } 361 | if (prevElement != null) { 362 | prevElement.appendChild(element); 363 | } else { 364 | if (element.getLocalName() != null && !StringUtils.contains(element.getLocalName(), "null")) { 365 | doc.appendChild(element); 366 | } 367 | } 368 | prevElement = element; 369 | } 370 | elemPos++; 371 | } 372 | } 373 | } 374 | if (parameterJsonPathMapping.isEmpty()) { 375 | for (String queryParam : queryPathParamMapping.keySet()) { 376 | Element element = doc.createElementNS(namespace, 377 | SOAPToRESTConstants.NAMESPACE_PREFIX + 378 | SOAPToRESTConstants.NAMESPACE_SEPARATOR + queryParam); 379 | element.setTextContent("${uri.var." + queryParam + "}"); 380 | if (rootElement != null) { 381 | rootElement.appendChild(element); 382 | } else { 383 | doc.appendChild(element); 384 | } 385 | } 386 | } else if (queryPathParamMapping.size() > 0) { 387 | log.warn("Query parameters along with the body parameter is not allowed"); 388 | } 389 | } catch (ParserConfigurationException e) { 390 | throw new SOAPToRESTException("Error occurred when building in sequence xml", e); 391 | } 392 | if (log.isDebugEnabled()) { 393 | log.debug("parameter mapping for used in payload factory for soap operation:" + operationId + " is " + 394 | stringWriter); 395 | } 396 | 397 | return doc; 398 | } 399 | 400 | private static String getXpath(Node node) { 401 | 402 | if (node != null) { 403 | Node parent = node.getParentNode(); 404 | if (parent == null && node.getLocalName() != null) { 405 | return node.getLocalName(); 406 | } else if (node.getLocalName() != null) { 407 | return getXpath(parent) + SOAPToRESTConstants.PATH_SEPARATOR + node.getLocalName(); 408 | } else { 409 | return getXpath(parent); 410 | } 411 | } 412 | return SOAPToRESTConstants.EMPTY_STRING; 413 | } 414 | 415 | /** 416 | * Escape the FreeMarker template. Since FreeMarker 2.3.22 the variable name can also contain minus (-), dot (.) 417 | * , and colon (:) at any position, but these must be escaped with a preceding backslash (\) 418 | * 419 | * @param template free marker template 420 | * @return escaped template 421 | */ 422 | private static String escapeFreeMarkerTemplate(String template) { 423 | 424 | return template.replace("-", "\\-").replace(".", "\\.") 425 | .replace(":", "\\:"); 426 | } 427 | } 428 | --------------------------------------------------------------------------------