Repository: tefra/xsdata

Issue:
operation name not being used in soap body when generating from wsdl
I'm seeing that the wsdl operation is coming from the input body name.  In this case it's `loginRequest` and that's what I'm seeing in the soap message `<ns1:loginRequest xmlns:ns1="http://microfocus.com/nas/2020/08">`

If I change the metadata name to `login` then the soap message changes and it works `<ns1:login xmlns:ns1="http://microfocus.com/nas/2020/08">`

```python
    @dataclass
    class Body:
        login_request: Optional[LoginRequest] = field(
            default=None,
            metadata={
                "name": "loginRequest",
                "type": "Element",
                "namespace": "http://microfocus.com/nas/2020/08",
            }
        )
```
The wsdl operation name is `login` and the service I'm calling will only accept `login`.
```xml
        <wsdl:operation name="login">
            <wsdl:documentation></wsdl:documentation>
            <wsdl:input message="nas:loginRequest" />
            <wsdl:output message="nas:loginResponse" />
            <wsdl:fault name="error" message="nas:nasFaultMessage" />
        </wsdl:operation>
```
full wsdl is attached to https://github.com/tefra/xsdata/issues/599#issuecomment-957345332

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=609&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=609&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=609&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=609&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=609) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=609&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=609&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/xsdata/codegen/mappers/definitions.py b/xsdata/codegen/mappers/definitions.py
index ece5309f..1ea77bd2 100644
--- a/xsdata/codegen/mappers/definitions.py
+++ b/xsdata/codegen/mappers/definitions.py
@@ -133,15 +133,19 @@ class DefinitionsMapper:
     ) -> Iterator[Class]:
         """Step 5: Map the BindingOperation messages to classes."""
 
-        messages: List[Tuple[str, BindingMessage, PortTypeMessage]] = []
+        messages: List[Tuple[str, BindingMessage, PortTypeMessage, Optional[str]]] = []
 
         if operation.input:
-            messages.append(("input", operation.input, port_type_operation.input))
+            messages.append(
+                ("input", operation.input, port_type_operation.input, operation.name)
+            )
 
         if operation.output:
-            messages.append(("output", operation.output, port_type_operation.output))
+            messages.append(
+                ("output", operation.output, port_type_operation.output, None)
+            )
 
-        for suffix, binding_message, port_type_message in messages:
+        for suffix, binding_message, port_type_message, operation_name in messages:
 
             if style == "rpc":
                 yield cls.build_message_class(definitions, port_type_message)
@@ -153,6 +157,7 @@ class DefinitionsMapper:
                 f"{name}_{suffix}",
                 style,
                 namespace,
+                operation_name,
             )
 
             if suffix == "output":
@@ -201,6 +206,7 @@ class DefinitionsMapper:
         name: str,
         style: str,
         namespace: Optional[str],
+        operation: Optional[str],
     ) -> Class:
         """Step 6.1: Build Envelope class for the given binding message with
         attributes from the port type message."""
@@ -224,7 +230,9 @@ class DefinitionsMapper:
 
             if style == "rpc" and class_name == "Body":
                 namespace = ext.attributes.get("namespace")
-                attrs = cls.map_port_type_message(port_type_message, namespace)
+                attrs = cls.map_port_type_message(
+                    operation, port_type_message, namespace
+                )
             else:
                 attrs = cls.map_binding_message_parts(
                     definitions, message, ext, inner.ns_map
@@ -283,13 +291,20 @@ class DefinitionsMapper:
 
     @classmethod
     def map_port_type_message(
-        cls, message: PortTypeMessage, namespace: Optional[str]
+        cls,
+        operation: Optional[str],
+        message: PortTypeMessage,
+        namespace: Optional[str],
     ) -> Iterator[Attr]:
         """Build an attribute for the given port type message."""
         prefix, name = text.split(message.message)
         source_namespace = message.ns_map.get(prefix)
+
+        if operation is None:
+            operation = name
+
         yield cls.build_attr(
-            name,
+            operation,
             qname=namespaces.build_qname(source_namespace, name),
             namespace=namespace,
         )


Test patch:
diff --git a/tests/codegen/mappers/test_definitions.py b/tests/codegen/mappers/test_definitions.py
index 9d2bdcd4..a285934c 100644
--- a/tests/codegen/mappers/test_definitions.py
+++ b/tests/codegen/mappers/test_definitions.py
@@ -252,6 +252,7 @@ class DefinitionsMapperTests(FactoryTestCase):
                     f"{name}_input",
                     style,
                     namespace,
+                    None,
                 ),
                 mock.call(
                     definitions,
@@ -260,6 +261,7 @@ class DefinitionsMapperTests(FactoryTestCase):
                     f"{name}_input",
                     style,
                     namespace,
+                    None,
                 ),
                 mock.call(
                     definitions,
@@ -268,6 +270,7 @@ class DefinitionsMapperTests(FactoryTestCase):
                     f"{name}_output",
                     style,
                     namespace,
+                    None,
                 ),
             ]
         )
@@ -293,6 +296,7 @@ class DefinitionsMapperTests(FactoryTestCase):
         mock_build_envelope_class.return_value = target
 
         operation.input = BindingMessage()
+        operation.name = "Add"
         port_operation.input = PortTypeMessage()
 
         result = DefinitionsMapper.map_binding_operation_messages(
@@ -311,6 +315,7 @@ class DefinitionsMapperTests(FactoryTestCase):
             f"{name}_input",
             style,
             namespace,
+            "Add",
         )
 
     @mock.patch.object(DefinitionsMapper, "map_port_type_message")
@@ -340,7 +345,13 @@ class DefinitionsMapperTests(FactoryTestCase):
         binding_message.ns_map["foo"] = "bar"
 
         result = DefinitionsMapper.build_envelope_class(
-            definitions, binding_message, port_type_message, name, style, namespace
+            definitions,
+            binding_message,
+            port_type_message,
+            name,
+            style,
+            namespace,
+            None,
         )
 
         expected = Class(
@@ -416,7 +427,13 @@ class DefinitionsMapperTests(FactoryTestCase):
         binding_message.ns_map["foo"] = "bar"
 
         result = DefinitionsMapper.build_envelope_class(
-            definitions, binding_message, port_type_message, name, style, namespace
+            definitions,
+            binding_message,
+            port_type_message,
+            name,
+            style,
+            namespace,
+            None,
         )
 
         expected = Class(
@@ -431,7 +448,9 @@ class DefinitionsMapperTests(FactoryTestCase):
         self.assertEqual(1, len(result.inner[0].attrs))
         self.assertEqual(2, len(result.inner[1].attrs))
         self.maxDiff = None
-        mock_map_port_type_message.assert_called_once_with(port_type_message, "bodyns")
+        mock_map_port_type_message.assert_called_once_with(
+            None, port_type_message, "bodyns"
+        )
 
         mock_map_binding_message_parts.assert_has_calls(
             [
@@ -735,7 +754,7 @@ class DefinitionsMapperTests(FactoryTestCase):
         target_namespace = "xsdata"
 
         actual = DefinitionsMapper.map_port_type_message(
-            port_type_message, target_namespace
+            None, port_type_message, target_namespace
         )
         expected = DefinitionsMapper.build_attr(
             "bar", qname=build_qname("foobar", "bar"), namespace=target_namespace
@@ -744,6 +763,16 @@ class DefinitionsMapperTests(FactoryTestCase):
         self.assertIsInstance(actual, Generator)
         self.assertEqual([expected], list(actual))
 
+        actual = DefinitionsMapper.map_port_type_message(
+            "Add", port_type_message, target_namespace
+        )
+        expected = DefinitionsMapper.build_attr(
+            "Add", qname=build_qname("foobar", "bar"), namespace=target_namespace
+        )
+
+        self.assertIsInstance(actual, Generator)
+        self.assertEqual([expected], list(actual))
+
     def test_operation_namespace(self):
 
         self.assertIsNone(DefinitionsMapper.operation_namespace({}))