def test_rebar_can_be_url_prefixed(self): app = Flask(__name__) app.testing = True rebar = Rebar() registry_v1 = rebar.create_handler_registry(prefix="v1") registry_v2 = rebar.create_handler_registry( prefix="/v2/") # Slashes shouldn't matter # We use the same endpoint to show that the swagger operationId gets set correctly # and the Flask endpoint gets prefixed register_endpoint(registry=registry_v1, endpoint="get_foo") register_endpoint(registry=registry_v2, endpoint="get_foo") rebar.init_app(app) for prefix in ("v1", "v2"): resp = app.test_client().get( prefix_url(prefix=prefix, url="/swagger/ui/")) self.assertEqual(resp.status_code, 200) swagger = get_swagger(test_client=app.test_client(), prefix=prefix) validate_swagger(swagger) self.assertEqual( swagger["paths"][prefix_url( prefix=prefix, url="/foos/{foo_uid}")]["get"]["operationId"], "get_foo", ) resp = app.test_client().get( prefix_url(prefix=prefix, url="/foos/1")) self.assertEqual(resp.status_code, 200)
def test_rebar_can_be_url_prefixed(self): app = Flask(__name__) app.testing = True rebar = Rebar() registry_v1 = rebar.create_handler_registry(prefix='v1') registry_v2 = rebar.create_handler_registry( prefix='/v2/') # Slashes shouldn't matter # We use the same endpoint to show that the swagger operationId gets set correctly # and the Flask endpoint gets prefixed register_endpoint(registry=registry_v1, endpoint='get_foo') register_endpoint(registry=registry_v2, endpoint='get_foo') rebar.init_app(app) for prefix in ('v1', 'v2'): resp = app.test_client().get( prefix_url(prefix=prefix, url='/swagger/ui/')) self.assertEqual(resp.status_code, 200) swagger = get_swagger(test_client=app.test_client(), prefix=prefix) validate_swagger(swagger) self.assertEqual( swagger['paths'][prefix_url( prefix=prefix, url='/foos/{foo_uid}')]['get']['operationId'], 'get_foo') resp = app.test_client().get( prefix_url(prefix=prefix, url='/foos/1')) self.assertEqual(resp.status_code, 200)
def test_swagger_endpoint_is_automatically_created(self): rebar = Rebar() rebar.create_handler_registry() app = create_rebar_app(rebar) resp = app.test_client().get('/swagger') self.assertEqual(resp.status_code, 200) validate_swagger(get_json_from_resp(resp))
def test_swagger_can_be_set_to_v3(self): rebar = Rebar() rebar.create_handler_registry(swagger_generator=SwaggerV3Generator()) app = create_rebar_app(rebar) resp = app.test_client().get("/swagger") self.assertEqual(resp.status_code, 200) validate_swagger(resp.json, SWAGGER_V3_JSONSCHEMA) resp = app.test_client().get("/swagger/ui/") self.assertEqual(resp.status_code, 200)
def test_swagger_generator_v2(registry, swagger_version, expected_swagger): if swagger_version == 2: swagger_jsonschema = SWAGGER_V2_JSONSCHEMA generator = SwaggerV2Generator() elif swagger_version == 3: swagger_jsonschema = SWAGGER_V3_JSONSCHEMA generator = SwaggerV3Generator() else: raise ValueError("Unknown swagger_version: {}".format(swagger_version)) validate_swagger(expected_swagger, schema=swagger_jsonschema) swagger = generator.generate(registry) result = json.dumps(swagger, indent=2, sort_keys=True) expected = json.dumps(expected_swagger, indent=2, sort_keys=True) assert result == expected
def test_swagger_generators(registry, swagger_generator, expected_swagger): open_api_version = swagger_generator.get_open_api_version() if open_api_version == "2.0": swagger_jsonschema = SWAGGER_V2_JSONSCHEMA elif open_api_version == "3.0.2": swagger_jsonschema = SWAGGER_V3_JSONSCHEMA else: raise ValueError( "Unknown swagger_version: {}".format(open_api_version)) validate_swagger(expected_swagger, schema=swagger_jsonschema) swagger = swagger_generator.generate(registry) result = json.dumps(swagger, indent=2, sort_keys=True) expected = json.dumps(expected_swagger, indent=2, sort_keys=True) assert result == expected
def test_swagger_v2_generator_non_registry_parameters(): host = "swag.com" schemes = ["http"] consumes = ["application/json"] produces = ["application/json"] title = "Test API" version = "2.1.0" description = "Foo Bar Baz" class Error(m.Schema): message = m.fields.String() details = m.fields.Dict() generator = SwaggerV2Generator( host=host, schemes=schemes, consumes=consumes, produces=produces, title=title, version=version, description=description, default_response_schema=Error(), tags=[ Tag( name="bar", description="baz", external_docs=ExternalDocumentation(url="http://bardocs.com", description="qux"), ) ], ) rebar = Rebar() registry = rebar.create_handler_registry() swagger = generator.generate(registry) expected_swagger = { "swagger": "2.0", "host": host, "info": { "title": title, "version": version, "description": description }, "schemes": schemes, "consumes": consumes, "produces": produces, "securityDefinitions": {}, "tags": [{ "name": "bar", "description": "baz", "externalDocs": { "url": "http://bardocs.com", "description": "qux" }, }], "paths": {}, "definitions": { "Error": { "type": "object", "title": "Error", "properties": { "message": { "type": "string" }, "details": { "type": "object" }, }, } }, } validate_swagger(expected_swagger) _assert_dicts_equal(swagger, expected_swagger)
def test_swagger_v3_generator_non_registry_parameters(): title = "Test API" version = "3.0.0" description = "testing testing 123" class Error(m.Schema): message = m.fields.String() details = m.fields.Dict() generator = SwaggerV3Generator( version=version, title=title, description=description, default_response_schema=Error(), tags=[ Tag( name="bar", description="baz", external_docs=ExternalDocumentation(url="http://bardocs.com", description="qux"), ) ], servers=[ Server( url="https://{username}.gigantic-server.com:{port}/{basePath}", description="The production API server", variables={ "username": ServerVariable( default="demo", description= "this value is assigned by the service provider, in this example `gigantic-server.com`", ), "port": ServerVariable(default="8443", enum=["8443", "443"]), "basePath": ServerVariable(default="v2"), }, ) ], ) rebar = Rebar() registry = rebar.create_handler_registry() swagger = generator.generate(registry) expected_swagger = { "openapi": "3.0.2", "info": { "title": title, "version": version, "description": description }, "tags": [{ "name": "bar", "description": "baz", "externalDocs": { "url": "http://bardocs.com", "description": "qux" }, }], "servers": [{ "url": "https://{username}.gigantic-server.com:{port}/{basePath}", "description": "The production API server", "variables": { "username": { "default": "demo", "description": "this value is assigned by the service provider, in this example `gigantic-server.com`", }, "port": { "enum": ["8443", "443"], "default": "8443" }, "basePath": { "default": "v2" }, }, }], "paths": {}, "components": { "schemas": { "Error": { "type": "object", "title": "Error", "properties": { "message": { "type": "string" }, "details": { "type": "object" }, }, } } }, } validate_swagger(expected_swagger, SWAGGER_V3_JSONSCHEMA) _assert_dicts_equal(swagger, expected_swagger)
def test_generate_swagger(self): rebar = Rebar() registry = rebar.create_handler_registry() authenticator = HeaderApiKeyAuthenticator(header='x-auth') default_authenticator = HeaderApiKeyAuthenticator(header='x-another', name='default') class HeaderSchema(m.Schema): user_id = m.fields.String(load_from='x-userid', required=True) class FooSchema(m.Schema): __swagger_title__ = 'Foo' uid = m.fields.String() name = m.fields.String() class ListOfFooSchema(m.Schema): data = m.fields.Nested(FooSchema, many=True) class FooUpdateSchema(m.Schema): __swagger_title = 'FooUpdate' name = m.fields.String() class FooListSchema(m.Schema): name = m.fields.String() @registry.handles(rule='/foos/<uuid_string:foo_uid>', method='GET', marshal_schema={200: FooSchema()}, headers_schema=HeaderSchema()) def get_foo(foo_uid): """helpful description""" pass @registry.handles(rule='/foos/<foo_uid>', method='PATCH', marshal_schema={200: FooSchema()}, request_body_schema=FooUpdateSchema(), authenticator=authenticator) def update_foo(foo_uid): pass @registry.handles( rule='/foos', method='GET', marshal_schema={200: ListOfFooSchema()}, query_string_schema=FooListSchema(), authenticator=None # Override the default! ) def list_foos(): pass registry.set_default_authenticator(default_authenticator) host = 'swag.com' schemes = ['http'] consumes = ['application/json'] produces = ['application/vnd.plangrid+json'] title = 'Test API' version = '2.1.0' class Error(m.Schema): message = m.fields.String() details = m.fields.Dict() generator = SwaggerV2Generator(host=host, schemes=schemes, consumes=consumes, produces=produces, title=title, version=version, default_response_schema=Error()) swagger = generator.generate(registry) expected_swagger = { 'swagger': '2.0', 'host': host, 'info': { 'title': title, 'version': version, 'description': '', }, 'schemes': schemes, 'consumes': consumes, 'produces': produces, 'security': [{ 'default': [] }], 'securityDefinitions': { 'sharedSecret': { 'type': 'apiKey', 'in': 'header', 'name': 'x-auth' }, 'default': { 'type': 'apiKey', 'in': 'header', 'name': 'x-another' } }, 'paths': { '/foos/{foo_uid}': { 'parameters': [{ 'name': 'foo_uid', 'in': 'path', 'required': True, 'type': 'string' }], 'get': { 'operationId': 'get_foo', 'description': 'helpful description', 'responses': { '200': { 'description': 'Foo', 'schema': { '$ref': '#/definitions/Foo' } }, 'default': { 'description': 'Error', 'schema': { '$ref': '#/definitions/Error' } } }, 'parameters': [{ 'name': 'x-userid', 'in': 'header', 'required': True, 'type': 'string' }] }, 'patch': { 'operationId': 'update_foo', 'responses': { '200': { 'description': 'Foo', 'schema': { '$ref': '#/definitions/Foo' } }, 'default': { 'description': 'Error', 'schema': { '$ref': '#/definitions/Error' } } }, 'parameters': [{ 'name': 'FooUpdateSchema', 'in': 'body', 'required': True, 'schema': { '$ref': '#/definitions/FooUpdateSchema' } }], 'security': [{ 'sharedSecret': [] }] } }, '/foos': { 'get': { 'operationId': 'list_foos', 'responses': { '200': { 'description': 'ListOfFooSchema', 'schema': { '$ref': '#/definitions/ListOfFooSchema' } }, 'default': { 'description': 'Error', 'schema': { '$ref': '#/definitions/Error' } } }, 'parameters': [{ 'name': 'name', 'in': 'query', 'required': False, 'type': 'string' }], 'security': [] } } }, 'definitions': { 'Foo': { 'type': 'object', 'title': 'Foo', 'properties': { 'uid': { 'type': 'string' }, 'name': { 'type': 'string' } } }, 'FooUpdateSchema': { 'type': 'object', 'title': 'FooUpdateSchema', 'properties': { 'name': { 'type': 'string' } } }, 'ListOfFooSchema': { 'type': 'object', 'title': 'ListOfFooSchema', 'properties': { 'data': { 'type': 'array', 'items': { '$ref': '#/definitions/Foo' } } } }, 'Error': { 'type': 'object', 'title': 'Error', 'properties': { 'message': { 'type': 'string' }, 'details': { 'type': 'object' } } } } } # Uncomment these lines to just dump the result to the terminal: # import json # print(json.dumps(swagger, indent=2)) # self.assertTrue(False) # This will raise an error if validation fails validate_swagger(expected_swagger) self.assertEqual(swagger, expected_swagger)
def test_generate_swagger(self): rebar = Rebar() registry = rebar.create_handler_registry() authenticator = HeaderApiKeyAuthenticator(header="x-auth") default_authenticator = HeaderApiKeyAuthenticator( header="x-another", name="default" ) class HeaderSchema(m.Schema): user_id = compat.set_data_key( field=m.fields.String(required=True), key="X-UserId" ) class FooSchema(m.Schema): __swagger_title__ = "Foo" uid = m.fields.String() name = m.fields.String() class NestedFoosSchema(m.Schema): data = m.fields.Nested(FooSchema, many=True) class FooUpdateSchema(m.Schema): __swagger_title = "FooUpdate" name = m.fields.String() class NameAndOtherSchema(m.Schema): name = m.fields.String() other = m.fields.String() @registry.handles( rule="/foos/<uuid_string:foo_uid>", method="GET", marshal_schema={200: FooSchema()}, headers_schema=HeaderSchema(), ) def get_foo(foo_uid): """helpful description""" pass @registry.handles( rule="/foos/<foo_uid>", method="PATCH", marshal_schema={200: FooSchema()}, request_body_schema=FooUpdateSchema(), authenticator=authenticator, ) def update_foo(foo_uid): pass # Test using Schema(many=True) without using a nested Field. # https://github.com/plangrid/flask-rebar/issues/41 @registry.handles( rule="/foo_list", method="GET", marshal_schema={200: FooSchema(many=True)}, authenticator=None, # Override the default! ) def list_foos(): pass @registry.handles( rule="/foos", method="GET", marshal_schema={200: NestedFoosSchema()}, query_string_schema=NameAndOtherSchema(), authenticator=None, # Override the default! ) def nested_foos(): pass @registry.handles(rule="/tagged_foos", tags=["bar", "baz"]) def tagged_foos(): pass registry.set_default_authenticator(default_authenticator) host = "swag.com" schemes = ["http"] consumes = ["application/json"] produces = ["application/json"] title = "Test API" version = "2.1.0" class Error(m.Schema): message = m.fields.String() details = m.fields.Dict() generator = SwaggerV2Generator( host=host, schemes=schemes, consumes=consumes, produces=produces, title=title, version=version, default_response_schema=Error(), tags=[ Tag( name="bar", description="baz", external_docs=ExternalDocumentation( url="http://bardocs.com", description="qux" ), ) ], ) swagger = generator.generate(registry) expected_swagger = { "swagger": "2.0", "host": host, "info": {"title": title, "version": version, "description": ""}, "schemes": schemes, "consumes": consumes, "produces": produces, "security": [{"default": []}], "securityDefinitions": { "sharedSecret": {"type": "apiKey", "in": "header", "name": "x-auth"}, "default": {"type": "apiKey", "in": "header", "name": "x-another"}, }, "tags": [ { "name": "bar", "description": "baz", "externalDocs": {"url": "http://bardocs.com", "description": "qux"}, } ], "paths": { "/foos/{foo_uid}": { "parameters": [ { "name": "foo_uid", "in": "path", "required": True, "type": "string", } ], "get": { "operationId": "get_foo", "description": "helpful description", "responses": { "200": { "description": "Foo", "schema": {"$ref": "#/definitions/Foo"}, }, "default": { "description": "Error", "schema": {"$ref": "#/definitions/Error"}, }, }, "parameters": [ { "name": "X-UserId", "in": "header", "required": True, "type": "string", } ], }, "patch": { "operationId": "update_foo", "responses": { "200": { "description": "Foo", "schema": {"$ref": "#/definitions/Foo"}, }, "default": { "description": "Error", "schema": {"$ref": "#/definitions/Error"}, }, }, "parameters": [ { "name": "FooUpdateSchema", "in": "body", "required": True, "schema": {"$ref": "#/definitions/FooUpdateSchema"}, } ], "security": [{"sharedSecret": []}], }, }, "/foo_list": { "get": { "operationId": "list_foos", "responses": { "200": { "description": "Foo", "schema": { "type": "array", "items": {"$ref": "#/definitions/Foo"}, }, }, "default": { "description": "Error", "schema": {"$ref": "#/definitions/Error"}, }, }, "security": [], } }, "/foos": { "get": { "operationId": "nested_foos", "responses": { "200": { "description": "NestedFoosSchema", "schema": {"$ref": "#/definitions/NestedFoosSchema"}, }, "default": { "description": "Error", "schema": {"$ref": "#/definitions/Error"}, }, }, "parameters": [ { "name": "name", "in": "query", "required": False, "type": "string", }, { "name": "other", "in": "query", "required": False, "type": "string", }, ], "security": [], } }, "/tagged_foos": { "get": { "tags": ["bar", "baz"], "operationId": "tagged_foos", "responses": { "default": { "description": "Error", "schema": {"$ref": "#/definitions/Error"}, } }, } }, }, "definitions": { "Foo": { "type": "object", "title": "Foo", "properties": { "uid": {"type": "string"}, "name": {"type": "string"}, }, }, "FooUpdateSchema": { "type": "object", "title": "FooUpdateSchema", "properties": {"name": {"type": "string"}}, }, "NestedFoosSchema": { "type": "object", "title": "NestedFoosSchema", "properties": { "data": { "type": "array", "items": {"$ref": "#/definitions/Foo"}, } }, }, "Error": { "type": "object", "title": "Error", "properties": { "message": {"type": "string"}, "details": {"type": "object"}, }, }, }, } # Uncomment these lines to just dump the result to the terminal: # import json # print(json.dumps(swagger, indent=2)) # print(json.dumps(expected_swagger, indent=2)) # self.assertTrue(False) # This will raise an error if validation fails validate_swagger(expected_swagger) self.assertEqual(swagger, expected_swagger)