Repository: boto/botocore
Pull request #497 (2015-03-24 00:26:00)

Add a wire_name property to op model
Continuation of https://github.com/boto/botocore/pull/496

cc @kyleknap @danielgtaylor


Patch:
diff --git a/botocore/model.py b/botocore/model.py
index 329434f5a..a3a6fadbd 100644
--- a/botocore/model.py
+++ b/botocore/model.py
@@ -233,7 +233,7 @@ class ServiceModel(object):
             model = self._service_description['operations'][operation_name]
         except KeyError:
             raise OperationNotFoundError(operation_name)
-        return OperationModel(model, self)
+        return OperationModel(model, self, operation_name)
 
     @CachedProperty
     def documentation(self):
@@ -308,7 +308,7 @@ class ServiceModel(object):
 
 
 class OperationModel(object):
-    def __init__(self, operation_model, service_model):
+    def __init__(self, operation_model, service_model, name=None):
         """
 
         :type operation_model: dict
@@ -319,15 +319,53 @@ class OperationModel(object):
         :type service_model: botocore.model.ServiceModel
         :param service_model: The service model associated with the operation.
 
+        :type name: string
+        :param name: The operation name.  This is the operation name exposed to
+            the users of this model.  This can potentially be different from
+            the "wire_name", which is the operation name that *must* by
+            provided over the wire.  For example, given::
+
+               "CreateCloudFrontOriginAccessIdentity":{
+                 "name":"CreateCloudFrontOriginAccessIdentity2014_11_06",
+                  ...
+              }
+
+           The ``name`` would be ``CreateCloudFrontOriginAccessIdentity``,
+           but the ``self.wire_name`` would be
+           ``CreateCloudFrontOriginAccessIdentity2014_11_06``, which is the
+           value we must send in the corresponding HTTP request.
+
         """
         self._operation_model = operation_model
         self._service_model = service_model
+        self._api_name = name
         # Clients can access '.name' to get the operation name
         # and '.metadata' to get the top level metdata of the service.
-        self.name = operation_model.get('name')
+        self._wire_name = operation_model.get('name')
         self.metadata = service_model.metadata
         self.http = operation_model.get('http', {})
 
+    @CachedProperty
+    def name(self):
+        if self._api_name is not None:
+            return self._api_name
+        else:
+            return self.wire_name
+
+    @property
+    def wire_name(self):
+        """The wire name of the operation.
+
+        In many situations this is the same value as the
+        ``name``, value, but in some services, the operation name
+        exposed to the user is different from the operaiton name
+        we send across the wire (e.g cloudfront).
+
+        Any serialization code should use ``wire_name``.
+
+        """
+        return self._operation_model.get('name')
+
     @property
     def service_model(self):
         return self._service_model
diff --git a/botocore/serialize.py b/botocore/serialize.py
index 5662e8d4c..961e1b09d 100644
--- a/botocore/serialize.py
+++ b/botocore/serialize.py
@@ -445,7 +445,7 @@ class BaseRestSerializer(Serializer):
             # If there's a payload member, we serialized that
             # member to they body.
             body_params = parameters.get(payload_member)
-            if body_params is not None:
+            if body_params:
                 serialized['body'] = self._serialize_body_params(
                     body_params,
                     shape_members[payload_member])


Test patch:
diff --git a/tests/unit/protocols/input/rest-xml.json b/tests/unit/protocols/input/rest-xml.json
index fb9336928..0cbb96fd3 100644
--- a/tests/unit/protocols/input/rest-xml.json
+++ b/tests/unit/protocols/input/rest-xml.json
@@ -855,48 +855,6 @@
           "body": "",
           "uri": "/"
         }
-      },
-      {
-        "given": {
-          "http": {
-            "method": "POST",
-            "requestUri": "/"
-          },
-          "input": {
-            "shape": "InputShape",
-            "payload": "foo"
-          },
-          "name": "OperationName"
-        },
-        "params": {
-          "foo": {}
-        },
-        "serialized": {
-          "method": "POST",
-          "body": "<foo />",
-          "uri": "/"
-        }
-      },
-      {
-        "given": {
-          "http": {
-            "method": "POST",
-            "requestUri": "/"
-          },
-          "input": {
-            "shape": "InputShape",
-            "payload": "foo"
-          },
-          "name": "OperationName"
-        },
-        "params": {
-          "foo": null
-        },
-        "serialized": {
-          "method": "POST",
-          "body": "",
-          "uri": "/"
-        }
       }
     ]
   },
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index d169e29cd..7d01fa076 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -115,6 +115,31 @@ class TestOperationModelFromService(unittest.TestCase):
         }
         self.service_model = model.ServiceModel(self.model)
 
+    def test_api_name(self):
+        service_model = model.ServiceModel(self.model)
+        operation = model.OperationModel(
+            self.model['operations']['OperationName'], service_model, 'Foo')
+        self.assertEqual(operation.name, 'Foo')
+        self.assertEqual(operation.wire_name, 'OperationName')
+
+    def test_api_name_default(self):
+        service_model = model.ServiceModel(self.model)
+        operation = model.OperationModel(
+            self.model['operations']['OperationName'], service_model)
+        self.assertEqual(operation.name, 'OperationName')
+
+    def test_api_name_from_service(self):
+        service_model = model.ServiceModel(self.model)
+        operation = service_model.operation_model('OperationName')
+        self.assertEqual(operation.name, 'OperationName')
+
+    def test_api_name_from_service_model_when_differs_from_name(self):
+        self.model['operations']['Foo'] = \
+            self.model['operations']['OperationName']
+        service_model = model.ServiceModel(self.model)
+        operation = service_model.operation_model('Foo')
+        self.assertEqual(operation.name, 'Foo')
+
     def test_operation_input_model(self):
         service_model = model.ServiceModel(self.model)
         operation = service_model.operation_model('OperationName')