Repository: Yelp/swagger_spec_validator

Issue:
Validator does not check uniqueness of operation ids
According to the Swagger spec, the `operationId` of an operation object is:

> Unique string used to identify the operation. The id MUST be unique among all operations described in the API. Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is recommended to follow common programming naming conventions.

The validator does not currently check that `operationId`s are unique across the API.  This would be a helpful feature because some codegen tools fail if this constraint is not met.

Discussion:
ATRAN2: Looks good to me, I can merge it

Patch:
diff --git a/swagger_spec_validator/validator20.py b/swagger_spec_validator/validator20.py
index 339bbba..83fe9c0 100644
--- a/swagger_spec_validator/validator20.py
+++ b/swagger_spec_validator/validator20.py
@@ -331,7 +331,7 @@ def validate_apis(apis, deref):
     :raises: :py:class:`swagger_spec_validator.SwaggerValidationError`
     :raises: :py:class:`jsonschema.exceptions.ValidationError`
     """
-    operation_tag_to_operation_id_set = defaultdict(set)
+    operation_id_set = set()
 
     for api_name, api_body in iteritems(apis):
         api_body = deref(api_body)
@@ -356,16 +356,14 @@ def validate_apis(apis, deref):
             oper_tags = deref(oper_body.get('tags', [None]))
 
             # Check that, if this operation has an operationId defined,
-            # no other operation with a same tag also has that
-            # operationId.
+            # no other operation also has that operationId.
             operation_id = oper_body.get('operationId')
             if operation_id is not None:
-                for oper_tag in oper_tags:
-                    if operation_id in operation_tag_to_operation_id_set[oper_tag]:
-                        raise SwaggerValidationError(
-                            "Duplicate operationId: {}".format(operation_id)
-                        )
-                    operation_tag_to_operation_id_set[oper_tag].add(operation_id)
+                if operation_id in operation_id_set:
+                    raise SwaggerValidationError(
+                        "Duplicate operationId: {}".format(operation_id)
+                    )
+                operation_id_set.add(operation_id)
 
             oper_params = deref(oper_body.get('parameters', []))
             validate_duplicate_param(oper_params, deref)


Test patch:
diff --git a/tests/validator20/validate_apis_test.py b/tests/validator20/validate_apis_test.py
index 6f6b3b6..9ba0c82 100644
--- a/tests/validator20/validate_apis_test.py
+++ b/tests/validator20/validate_apis_test.py
@@ -256,8 +256,14 @@ def test_duplicate_operationIds_fails(apis):
         },
     ]
 )
-def test_duplicate_operationIds_succeeds_if_tags_differ(apis):
-    validate_apis(apis, lambda x: x)
+def test_duplicate_operationIds_fails_if_tags_differ(apis):
+    with pytest.raises(SwaggerValidationError) as excinfo:
+        validate_apis(apis, lambda x: x)
+
+    swagger_validation_error = excinfo.value
+    error_message = swagger_validation_error.args[0]
+
+    assert error_message == "Duplicate operationId: duplicateOperationId"
 
 
 def test_invalid_inline_models_in_responses_fails():