def test_array_and_map_of_enum(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; enum State { GOOD = 0; BAD = 1; } message Map { map<string, State> enum_map = 1; repeated State states = 2; } service RsvpService { rpc Get(Null) returns(Map) { option (google.api.http) = { get: "/maps" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.State': { u'default': u'GOOD', u'description': u'State\n' u'Valid values:\n' u' - GOOD\n' u' - BAD', u'type': u'string', u'enum': [u'GOOD', u'BAD'] }, u'test.Map': { u'type': u'object', u'properties': { u'enum_map': { u'type': u'object', u'additionalProperties': { u'$ref': u'#/definitions/test.State', } }, u'states': { u'type': u'array', u'items': { u'$ref': u'#/definitions/test.State' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_map_fields(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; message Maps { map<string, string> string_map = 1; map<string, int32> int32_map = 2; map<string, Null> object_map = 3; } service RsvpService { rpc Get(Null) returns(Maps) { option (google.api.http) = { get: "/maps" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.Maps': { u'type': u'object', u'properties': { u'string_map': { u'type': u'object', u'additionalProperties': { u'type': u'string', u'format': u'string' } }, u'int32_map': { u'type': u'object', u'additionalProperties': { u'type': u'integer', u'format': u'int32' } }, u'object_map': { u'type': u'object', u'additionalProperties': { u'$ref': u'#/definitions/test.Null', } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_array_of_simple_types(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; message Invitations { string event = 1; repeated string names = 2; repeated int32 ages = 3; } service RsvpService { rpc Get(Null) returns(Invitations) { option (google.api.http) = { get: "/invitations" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.Invitations': { u'type': u'object', u'properties': { u'event': { u'type': u'string', u'format': u'string' }, u'names': { u'type': u'array', u'items': { u'type': u'string', u'format': u'string' } }, u'ages': { u'type': u'array', u'items': { u'type': u'integer', u'format': u'int32' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_nested_enum(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; message Forecast { // Detailed weather state enum WeatherState { GOOD = 0; // Weather is good BAD = 1; // Weather is bad } WeatherState forecast = 1; } service ForecastService { rpc GetForecast(Null) returns(Forecast) { option (google.api.http) = { get: "/forecast" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.Forecast.WeatherState': { u'default': u'GOOD', u'description': u'Detailed weather state\n' u'Valid values:\n' u' - GOOD: Weather is good\n' u' - BAD: Weather is bad', u'type': u'string', u'enum': [u'GOOD', u'BAD'] }, u'test.Forecast': { u'type': u'object', u'properties': { u'forecast': { u'$ref': u'#/definitions/test.Forecast.WeatherState' } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_http_annotations_carryover_and_all_components(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {} service Test { rpc Call(Null) returns(Null) { option (google.api.http) = { get: "/some/path" }; } } """) expected = { u'syntax': 'proto3', u'name': u'test.proto', u'package': u'test', u'dependency': [u'google/api/annotations.proto'], u'message_type': [{ u'name': u'Null' }], u'service': [{ u'name': u'Test', u'method': [{ u'name': u'Call', u'input_type': u'.test.Null', u'output_type': u'.test.Null', u'options': { u'http': { u'get': u'/some/path' } } }] }] } request = generate_plugin_request(proto) assert len(request.proto_file) == 4 parser = DescriptorParser() native_data = parser.parse_file_descriptors(request.proto_file, fold_comments=True) self.assertEqual([d['name'] for d in native_data], [ u'google/api/http.proto', u'google/protobuf/descriptor.proto', u'google/api/annotations.proto', u'test.proto' ]) self.assertEqual(json_rt(native_data[3]), expected)
def test_path_parameter_handling(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Simple { string str = 1; int32 int = 2; } service TestService { rpc Get(Simple) returns(Simple) { option (google.api.http) = { get: "/test/{str}/{int}" }; } } """) expected_swagger_path = { u'/test/{str}/{int}': { u'get': { u'operationId': u'Get', u'parameters': [{ u'name': u'str', u'in': u'path', u'type': u'string', u'format': u'string', u'required': True }, { u'name': u'int', u'in': u'path', u'type': u'integer', u'format': u'int32', u'required': True }], u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/test.Simple' } } }, u'tags': [u'TestService'] } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['paths']), expected_swagger_path)
def test_empty_message_with_service(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {} service TestService { rpc Get(Null) returns(Null) { option (google.api.http) = { get: "/test" }; } } """) expected_swagger = { u'swagger': u'2.0', u'info': { u'title': u'test.proto', u'version': u"version not set" }, u'schemes': [u"http", u"https"], u'consumes': [u"application/json"], u'produces': [u"application/json"], u'paths': { u'/test': { u'get': { u'operationId': u'Get', u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/test.Null' } } }, u'tags': [u'TestService'] } } }, u'definitions': { u'test.Null': { u'type': u'object' } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger), expected_swagger)
def test_kitchen_sink(self): proto = load_file( os.path.dirname(__file__) + '/a_bit_of_everything.proto') swagger = self.gen_swagger(proto) expected_swagger = json.loads( load_file( os.path.dirname(__file__) + '/a_bit_of_everything.swagger.json')) self.maxDiff = 100000 self.assertEqual(json_rt(swagger), expected_swagger)
def test_recursively_nested_values(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; message TreeNode { string name = 1; repeated TreeNode children = 2; } service RsvpService { rpc Get(Null) returns(TreeNode) { option (google.api.http) = { get: "/invitations" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.TreeNode': { u'type': u'object', u'properties': { u'name': { u'type': u'string', u'format': u'string' }, u'children': { u'type': u'array', u'items': { u'$ref': u'#/definitions/test.TreeNode' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_empty_def(self): proto = unindent(""" syntax = "proto3"; package test; """) expected_swagger = { u'swagger': u'2.0', u'info': { u'title': u'test.proto', u'version': u'version not set' }, u'schemes': [u"http", u"https"], u'consumes': [u"application/json"], u'produces': [u"application/json"], u'paths': {}, u'definitions': {} } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger), expected_swagger)
def test_array_of_object_type(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; message Null {}; message Invitations { message Address { string street = 1; string city = 2; } string event = 1; repeated Null nulles = 2; repeated Address addresses = 3; } service RsvpService { rpc Get(Null) returns(Invitations) { option (google.api.http) = { get: "/invitations" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.Invitations.Address': { u'type': u'object', u'properties': { u'street': { u'type': u'string', u'format': u'string' }, u'city': { u'type': u'string', u'format': u'string' } } }, u'test.Invitations': { u'type': u'object', u'properties': { u'event': { u'type': u'string', u'format': u'string' }, u'nulles': { u'type': u'array', u'items': { u'$ref': u'#/definitions/test.Null' } }, u'addresses': { u'type': u'array', u'items': { u'$ref': u'#/definitions/test.Invitations.Address' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_nested_type_definitions(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; message Null {} message Outer { message Inner { message Innermost { bool healthy = 1; string illness = 2; } Innermost innermost = 1; string other = 2; } string name = 1; Inner inner = 2; } service TestService { rpc Get(Null) returns(Outer) { option (google.api.http) = { get: "/test" }; } } """) expected_swagger_definitions = { u'test.Null': { u'type': u'object' }, u'test.Outer': { u'type': u'object', u'properties': { u'inner': { u'$ref': u'#/definitions/test.Outer.Inner' }, u'name': { u'type': u'string', u'format': u'string' } } }, u'test.Outer.Inner': { u'type': u'object', u'properties': { u'innermost': { u'$ref': u'#/definitions/test.Outer.Inner.Innermost' }, u'other': { u'type': u'string', u'format': u'string' } } }, u'test.Outer.Inner.Innermost': { u'type': u'object', u'properties': { u'healthy': { u'type': u'boolean', u'format': u'boolean' }, u'illness': { u'type': u'string', u'format': u'string' } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger['definitions']), expected_swagger_definitions)
def test_google_null(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; service TestService { rpc Get(google.protobuf.Empty) returns(google.protobuf.Empty) { option (google.api.http) = { get: "/echo" }; } } """) expected_swagger = { u'swagger': u'2.0', u'info': { u'title': u'test.proto', u'version': u"version not set" }, u'schemes': [u"http", u"https"], u'consumes': [u"application/json"], u'produces': [u"application/json"], u'paths': { u'/echo': { u'get': { u'operationId': u'Get', u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/google.protobuf.Empty' } } }, u'tags': [u'TestService'] } } }, u'definitions': { u'google.protobuf.Empty': { u'description': u'A generic empty message that you can ' u're-use to avoid defining duplicated\n ' u'empty messages in your APIs. A typical ' u'example is to use it as the request\n ' u'or the response type of an API method. ' u'For instance:\n\n service Foo {\n ' u' rpc Bar(google.protobuf.Empty) ' u'returns (google.protobuf.Empty);\n ' u'}\n\n The JSON representation for ' u'`Empty` is empty JSON object `{}`.', u'type': u'object' } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger), expected_swagger)
def test_method_input_params_in_body(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; // Simple Message message Simple { string str = 1; // a string attribute int32 int = 2; // an int32 attribute } // Service to get things done service TestService { /* Get simple answer * * Returns the true answer to all of life's persistent questions. */ rpc Get(Simple) returns(Simple) { option (google.api.http) = { get: "/test" }; } /* * Make up an answer (notice the leading blank line) * * Define the ultimate answer */ rpc MakeUp(Simple) returns(Simple) { option (google.api.http) = { post: "/test" body: "*" }; } } """) expected_swagger = { u'swagger': u'2.0', u'info': { u'title': u'test.proto', u'version': u"version not set" }, u'schemes': [u"http", u"https"], u'consumes': [u"application/json"], u'produces': [u"application/json"], u'paths': { u'/test': { u'get': { u'summary': u'Get simple answer', u'description': u' Returns the true answer to all of life\'s ' u'persistent questions.', u'operationId': u'Get', u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/test.Simple' } } }, u'tags': [u'TestService'] }, u'post': { u'summary': u'Make up an answer (notice the leading ' u'blank line)', u'description': u' Define the ultimate answer', u'operationId': u'MakeUp', u'parameters': [{ u'name': u'body', u'in': u'body', u'required': True, u'schema': { u'$ref': u'#/definitions/test.Simple' } }], u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/test.Simple' } } }, u'tags': [u'TestService'] } } }, u'definitions': { u'test.Simple': { u'description': u'Simple Message', u'type': u'object', u'properties': { u'int': { u'description': u'an int32 attribute', u'type': u'integer', u'format': u'int32' }, u'str': { u'description': u'a string attribute', u'type': u'string', u'format': u'string' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger), expected_swagger)
def test_simple_annotated_message_with_simple_annotated_service(self): proto = unindent(""" syntax = "proto3"; package test; import "google/api/annotations.proto"; // Simple Message message Simple { string str = 1; // a string attribute int32 int = 2; // an int32 attribute } // Service to get things done service TestService { /* Get simple answer * * Returns the true answer to all of life's persistent questions. */ rpc Get(Simple) returns(Simple) { option (google.api.http) = { get: "/test" }; } } """) expected_swagger = { u'swagger': u'2.0', u'info': { u'title': u'test.proto', u'version': u"version not set" }, u'schemes': [u"http", u"https"], u'consumes': [u"application/json"], u'produces': [u"application/json"], u'paths': { u'/test': { u'get': { u'summary': u'Get simple answer', u'description': u' Returns the true answer to all of life\'s ' u'persistent questions.', u'operationId': u'Get', u'responses': { u'200': { u'description': u'', u'schema': { u'$ref': u'#/definitions/test.Simple' } } }, u'tags': [u'TestService'] } } }, u'definitions': { u'test.Simple': { u'description': u'Simple Message', u'type': u'object', u'properties': { u'int': { u'description': u'an int32 attribute', u'type': u'integer', u'format': u'int32' }, u'str': { u'description': u'a string attribute', u'type': u'string', u'format': u'string' } } } } } swagger = self.gen_swagger(proto) self.assertEqual(json_rt(swagger), expected_swagger)
def test_message_with_comment_folding(self): proto = unindent(""" syntax = "proto3"; package test; // Sample message message SampleMessage { string name = 1; // inline comment // prefix comment repeated int32 number = 2; bool bool = 3; // suffix comment } """) expected = { u'syntax': u'proto3', u'name': u'test.proto', u'package': u'test', u'message_type': [{ u'_description': u'Sample message', u'name': u'SampleMessage', u'field': [{ u'_description': u'inline comment', u'json_name': u'name', u'name': u'name', u'label': 1, u'number': 1, u'type': 9 }, { u'_description': u'prefix comment', u'json_name': u'number', u'name': u'number', u'label': 3, u'number': 2, u'type': 5 }, { u'_description': u'suffix comment', u'json_name': u'bool', u'name': u'bool', u'label': 1, u'number': 3, u'type': 8 }], }] } request = generate_plugin_request(proto) assert len(request.proto_file) == 1 parser = DescriptorParser() native_data = parser.parse_file_descriptor(request.proto_file[0], fold_comments=True) self.assertEqual(json_rt(native_data), expected)