Exemplo n.º 1
0
    def test_blueprint_arguments_documents_error_response(
            self, app, schemas, openapi_version, error_code
    ):
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')
        blp.ARGUMENTS_PARSER.DEFAULT_VALIDATION_STATUS = 400

        kwargs = {}
        if error_code:
            kwargs['error_status_code'] = error_code

        @blp.route('/')
        @blp.arguments(schemas.DocSchema, **kwargs)
        def func():
            """Dummy view func"""

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        error_code = error_code or 400
        assert (
            spec['paths']['/test/']['get']['responses'][str(error_code)] ==
            build_ref(api.spec, 'response', http.HTTPStatus(error_code).name)
        )
        assert http.HTTPStatus(error_code).name in get_responses(api.spec)
Exemplo n.º 2
0
    def test_pagination_item_count_missing(self, app, header_name):
        """If item_count was not set, pass and warn"""
        api = Api(app)

        class CustomBlueprint(Blueprint):
            PAGINATION_HEADER_FIELD_NAME = header_name

        blp = CustomBlueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.response()
        @blp.paginate()
        def func(pagination_parameters):
            # Here, we purposely forget to call set_item_count
            # pagination_parameters.item_count = 2
            return [1, 2]

        api.register_blueprint(blp)
        client = app.test_client()

        with mock.patch.object(app.logger, 'warning') as mock_warning:
            response = client.get('/test/')
            assert response.status_code == 200
            assert 'X-Pagination' not in response.headers
            if header_name is None:
                assert mock_warning.call_count == 0
            else:
                assert mock_warning.call_count == 1
                assert mock_warning.call_args == ((
                    'item_count not set in endpoint test.func', ), )
Exemplo n.º 3
0
    def test_pagination_custom_header_field_name(self, app, header_name):
        """Test PAGINATION_HEADER_FIELD_NAME overriding"""
        api = Api(app)

        class CustomBlueprint(Blueprint):
            PAGINATION_HEADER_FIELD_NAME = header_name

        blp = CustomBlueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.response()
        @blp.paginate()
        def func(pagination_parameters):
            pagination_parameters.item_count = 2
            return [1, 2]

        api.register_blueprint(blp)
        client = app.test_client()
        response = client.get('/test/')
        assert response.status_code == 200
        assert 'X-Pagination' not in response.headers
        if header_name is not None:
            assert response.headers[header_name] == (
                '{"total": 2, "total_pages": 1, '
                '"first_page": 1, "last_page": 1, "page": 1}')
            # Also check there is only one pagination header
            assert len(response.headers.getlist(header_name)) == 1
Exemplo n.º 4
0
 def test_api_extra_spec_kwargs_init_app_update_init(self, app):
     """Test empty APISpec kwargs passed in init_app update init kwargs"""
     api = Api(spec_kwargs={'basePath': '/v1', 'host': 'example.com'})
     api.init_app(app, spec_kwargs={'basePath': '/v2'})
     spec = api.spec.to_dict()
     assert spec['host'] == 'example.com'
     assert spec['basePath'] == '/v2'
Exemplo n.º 5
0
    def test_api_int_float_converter(self, app, params, nb_type,
                                     openapi_version):
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', 'test', url_prefix='/test')

        param, output = params

        @blp.route('/<{}{}:val>'.format(nb_type, param))
        def test(val):
            pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()

        schema = {
            'int': {
                'type': 'integer'
            },
            'float': {
                'type': 'number'
            },
        }[nb_type]
        schema.update(output)
        parameter = {'in': 'path', 'name': 'val', 'required': True}
        if openapi_version == '2.0':
            parameter.update(schema)
        else:
            parameter['schema'] = schema
        assert spec['paths']['/test/{val}']['parameters'] == [parameter]
Exemplo n.º 6
0
    def test_pagination_parameters_and_query_string_args(self, app, schemas):
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.arguments(schemas.QueryArgsSchema, location="query")
        @blp.response()
        @blp.paginate(Page)
        def func(query_args):
            assert query_args['arg1'] == 'Test'
            assert query_args['arg2'] == 12
            return range(30)

        api.register_blueprint(blp)
        client = app.test_client()

        # Pagination params in query string: OK
        response = client.get('/test/',
                              query_string={
                                  'page': 2,
                                  'page_size': 20,
                                  'arg1': 'Test',
                                  'arg2': 12
                              })
        assert response.json == list(range(20, 30))
Exemplo n.º 7
0
    def test_api_register_field_parameters(self, app, mapping):
        api = Api(app)

        class CustomField(ma.fields.Field):
            pass

        api.register_field(CustomField, *mapping)

        class Document(ma.Schema):
            field = CustomField()

        api.spec.components.schema('Document', schema=Document)

        if len(mapping) == 2:
            properties = {'field': {'type': 'custom string'}}
            # If mapping format is None, it does not appear in the spec
            if mapping[1] is not None:
                properties['field']['format'] = mapping[1]
        else:
            properties = {'field': {'type': 'integer'}}

        assert get_schemas(api.spec)['Document'] == {
            'properties': properties,
            'type': 'object'
        }
    def test_pagination_parameters_and_query_string_args(self, app, schemas):
        api = Api(app)
        blp = Blueprint("test", __name__, url_prefix="/test")

        @blp.route("/")
        @blp.arguments(schemas.QueryArgsSchema, location="query")
        @blp.response(200)
        @blp.paginate(Page)
        def func(query_args):
            assert query_args["arg1"] == "Test"
            assert query_args["arg2"] == 12
            return range(30)

        api.register_blueprint(blp)
        client = app.test_client()

        # Pagination params in query string: OK
        response = client.get(
            "/test/",
            query_string={
                "page": 2,
                "page_size": 20,
                "arg1": "Test",
                "arg2": 12
            },
        )
        assert response.json == list(range(20, 30))
Exemplo n.º 9
0
    def test_blueprint_doc_merged_after_prepare_doc(self, app):
        app.config['OPENAPI_VERSION'] = '3.0.2'
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        # This is a dummy example. In real-life, use 'example' parameter.
        doc_example = {
            'content': {'application/json': {'example': {'test': 123}}}}

        class ItemSchema(ma.Schema):
            test = ma.fields.Int()

        @blp.route('/')
        class Resource(MethodView):

            @blp.doc(**{'requestBody': doc_example})
            @blp.doc(**{'responses': {200: doc_example}})
            @blp.arguments(ItemSchema)
            @blp.response(ItemSchema)
            def get(self):
                pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        get = spec['paths']['/test/']['get']
        assert get['requestBody']['content']['application/json'][
            'example'] == {'test': 123}
        resp = get['responses']['200']
        assert resp['content']['application/json']['example'] == {'test': 123}
        assert 'schema' in resp['content']['application/json']
Exemplo n.º 10
0
    def test_api_int_float_converter(self, app, params, nb_type,
                                     openapi_version):
        app.config["OPENAPI_VERSION"] = openapi_version
        api = Api(app)
        blp = Blueprint("test", "test", url_prefix="/test")

        param, output = params

        @blp.route(f"/<{nb_type}{param}:val>")
        def test(val):
            pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()

        schema = {
            "int": {
                "type": "integer"
            },
            "float": {
                "type": "number"
            },
        }[nb_type]
        schema.update(output)
        parameter = {"in": "path", "name": "val", "required": True}
        if openapi_version == "2.0":
            parameter.update(schema)
        else:
            parameter["schema"] = schema
        assert spec["paths"]["/test/{val}"]["parameters"] == [parameter]
    def test_pagination_header_documentation(self, app, openapi_version):
        """Test pagination header is documented"""
        app.config["OPENAPI_VERSION"] = openapi_version
        api = Api(app)

        class CustomBlueprint(Blueprint):
            PAGINATION_HEADER_NAME = "X-Custom-Pagination-Header"

        blp = CustomBlueprint("test", __name__, url_prefix="/test")

        @blp.route("/")
        @blp.response(200)
        @blp.paginate()
        def func(pagination_parameters):
            """Dummy view func"""

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        get = spec["paths"]["/test/"]["get"]
        assert "PaginationMetadata" in get_schemas(api.spec)
        if openapi_version == "2.0":
            assert get["responses"]["200"]["headers"] == {
                "X-Custom-Pagination-Header": {
                    "description": "Pagination metadata",
                    "schema": {
                        "$ref": "#/definitions/PaginationMetadata"
                    },
                }
            }
        else:
            assert get["responses"]["200"]["headers"] == {
                "X-Custom-Pagination-Header": {
                    "$ref": "#/components/headers/PAGINATION"
                }
            }
Exemplo n.º 12
0
 def test_api_extra_spec_kwargs_init_app_update_init(self, app):
     """Test empty APISpec kwargs passed in init_app update init kwargs"""
     api = Api(spec_kwargs={"basePath": "/v1", "host": "example.com"})
     api.init_app(app, spec_kwargs={"basePath": "/v2"})
     spec = api.spec.to_dict()
     assert spec["host"] == "example.com"
     assert spec["basePath"] == "/v2"
Exemplo n.º 13
0
    def test_api_register_field_parameters(self, app, mapping):
        api = Api(app)

        class CustomField(ma.fields.Field):
            pass

        api.register_field(CustomField, *mapping)

        class Document(ma.Schema):
            field = CustomField()

        api.spec.components.schema("Document", schema=Document)

        if len(mapping) == 2:
            properties = {"field": {"type": "custom string"}}
            # If mapping format is None, it does not appear in the spec
            if mapping[1] is not None:
                properties["field"]["format"] = mapping[1]
        else:
            properties = {"field": {"type": "integer"}}

        assert get_schemas(api.spec)["Document"] == {
            "properties": properties,
            "type": "object",
        }
Exemplo n.º 14
0
    def test_api_register_converter_before_and_after_init(
            self, app, openapi_version):
        api = Api()
        blp = Blueprint('test', 'test', url_prefix='/test')

        class CustomConverter_1(BaseConverter):
            pass

        class CustomConverter_2(BaseConverter):
            pass

        app.url_map.converters['custom_str_1'] = CustomConverter_1
        app.url_map.converters['custom_str_2'] = CustomConverter_2
        api.register_converter(CustomConverter_1, 'custom string 1')
        api.init_app(app)
        api.register_converter(CustomConverter_2, 'custom string 2')

        @blp.route('/1/<custom_str_1:val>')
        def test_func_1(val):
            pass

        @blp.route('/2/<custom_str_2:val>')
        def test_func_2(val):
            pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        parameter_1 = spec['paths']['/test/1/{val}']['parameters'][0]
        parameter_2 = spec['paths']['/test/2/{val}']['parameters'][0]
        if 'openapi_version' == '2.0':
            assert parameter_1['type'] == 'custom string 1'
            assert parameter_2['type'] == 'custom string 2'
        else:
            assert parameter_1['schema']['type'] == 'custom string 1'
            assert parameter_2['schema']['type'] == 'custom string 2'
Exemplo n.º 15
0
    def test_pagination_item_count_missing(self, app, header_name):
        """If item_count was not set, pass and warn"""
        api = Api(app)

        class CustomBlueprint(Blueprint):
            PAGINATION_HEADER_FIELD_NAME = header_name

        blp = CustomBlueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.response()
        @blp.paginate()
        def func(pagination_parameters):
            # Here, we purposely forget to set item_count
            # pagination_parameters.item_count = 2
            return [1, 2]

        api.register_blueprint(blp)
        client = app.test_client()

        with pytest.warns(None) as record:
            response = client.get('/test/')
        if header_name is None:
            assert not record
        else:
            assert len(record) == 1
            assert record[0].category == UserWarning
            assert str(record[0].message) == (
                'item_count not set in endpoint test.func.')
        assert response.status_code == 200
        assert 'X-Pagination' not in response.headers
Exemplo n.º 16
0
    def test_api_lazy_registers_pagination_header(self, app):
        """Test pagination header is registered"""
        api = Api(app)

        # Declare dummy header to ensure get_headers doesn't fail
        header_1 = {"description": "Header 1"}
        api.spec.components.header("Header_1", header_1)

        # No route registered -> parameter header not registered
        headers = get_headers(api.spec)
        assert headers == {"Header_1": header_1}

        # Register routes with pagination
        blp = Blueprint("test", "test", url_prefix="/test")

        @blp.route("/")
        @blp.response(200)
        @blp.paginate()
        def test_get(val):
            pass

        api.register_blueprint(blp)

        headers = get_headers(api.spec)
        assert headers["PAGINATION"] == {
            "description": "Pagination metadata",
            "schema": {"$ref": "#/components/schemas/PaginationMetadata"},
        }
Exemplo n.º 17
0
    def test_pagination_parameters_not_in_query_string(self, app):
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.response()
        @blp.paginate(Page)
        def func():
            return range(30)

        api.register_blueprint(blp)
        client = app.test_client()

        # Pagination params in query string: OK
        response = client.get('/test/',
                              query_string={
                                  'page': 2,
                                  'page_size': 20
                              })
        assert response.json == list(range(20, 30))

        # Pagination params in another location are ignored
        response = client.get(
            '/test/',
            data=json.dumps({
                'page': 2,
                'page_size': 20
            }),
        )
        assert response.json == list(range(0, 10))
Exemplo n.º 18
0
    def test_pagination_header_documentation(self, app):
        """Test pagination header is documented"""
        api = Api(app)

        class CustomBlueprint(Blueprint):
            PAGINATION_HEADER_FIELD_NAME = 'X-Custom-Pagination-Header'

        blp = CustomBlueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        @blp.response()
        @blp.paginate()
        def func(pagination_parameters):
            """Dummy view func"""

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        get = spec['paths']['/test/']['get']
        assert 'PaginationMetadata' in get_schemas(api.spec)
        assert get['responses']['200']['headers'] == {
            'X-Custom-Pagination-Header': {
                'description': 'Pagination metadata',
                'schema': {
                    '$ref': '#/components/schemas/PaginationMetadata'
                },
            }
        }
Exemplo n.º 19
0
    def test_blueprint_doc_function(self, app):
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')
        client = app.test_client()

        @blp.route('/', methods=(
            'PUT',
            'PATCH',
        ))
        @blp.doc(summary='Dummy func', description='Do dummy stuff')
        def view_func():
            return {'Value': 'OK'}

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        path = spec['paths']['/test/']
        for method in (
                'put',
                'patch',
        ):
            assert path[method]['summary'] == 'Dummy func'
            assert path[method]['description'] == 'Do dummy stuff'

        response = client.put('/test/')
        assert response.status_code == 200
        assert response.json == {'Value': 'OK'}
Exemplo n.º 20
0
    def test_blueprint_alt_response_wrapper(self, app, schemas,
                                            openapi_version):
        """Check alt_response passes response transparently"""
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        api.spec.components.response('ClientErrorResponse')

        blp = Blueprint('test', 'test', url_prefix='/test')
        client = app.test_client()

        @blp.route('/')
        @blp.response(200, schemas.DocSchema)
        @blp.alt_response(400, "ClientErrorResponse")
        def func():
            return {'item_id': 12}

        api.register_blueprint(blp)

        paths = api.spec.to_dict()['paths']

        response_ref = build_ref(api.spec, 'response', 'ClientErrorResponse')

        assert paths['/test/']['get']['responses']['400'] == response_ref

        resp = client.get('test/')
        assert resp.json == {'item_id': 12}
Exemplo n.º 21
0
    def test_blueprint_etag_documents_responses(
            self, app, method, decorate, etag_disabled,
    ):
        app.config['ETAG_DISABLED'] = etag_disabled
        api = Api(app)
        blp = Blueprint('test', 'test', url_prefix='/test')

        if decorate:
            @blp.route('/', methods=[method])
            @blp.etag
            def func():
                pass
        else:
            @blp.route('/', methods=[method])
            def func():
                pass

        api.register_blueprint(blp)

        operation = api.spec.to_dict()['paths']['/test/'][method.lower()]
        responses = operation.get('responses', {})

        if not decorate or etag_disabled:
            assert '304' not in responses
            assert '412' not in responses
            assert '428' not in responses
        else:
            assert ('304' in responses) == (method in ['GET', 'HEAD'])
            assert ('412' in responses) == (
                method in ['PUT', 'PATCH', 'DELETE'])
            assert ('428' in responses) == (
                method in ['PUT', 'PATCH', 'DELETE'])
Exemplo n.º 22
0
    def test_blueprint_response_examples(self, app, openapi_version):
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', 'test', url_prefix='/test')

        examples = {
            'example 1': {
                'summary': 'Example 1',
                'value': {
                    'name': 'One'
                }
            },
            'example 2': {
                'summary': 'Example 2',
                'value': {
                    'name': 'Two'
                }
            },
        }

        @blp.route('/')
        @blp.response(200, examples=examples)
        def func():
            pass

        api.register_blueprint(blp)

        get = api.spec.to_dict()['paths']['/test/']['get']
        assert get['responses']['200']['content']['application/json'][
            'examples'] == examples
Exemplo n.º 23
0
    def test_blueprint_multiple_alt_response(self, app, openapi_version,
                                             schemas):
        """Check multiple nested calls to alt_response"""
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', 'test', url_prefix='/test')

        @blp.route('/')
        @blp.alt_response(400, schemas.ClientErrorSchema)
        @blp.alt_response(404, 'NotFoundErrorResponse')
        def func():
            pass

        api.register_blueprint(blp)

        paths = api.spec.to_dict()['paths']

        schema_ref = build_ref(api.spec, 'schema', 'ClientError')
        response_ref = build_ref(api.spec, 'response', 'NotFoundErrorResponse')

        response = paths['/test/']['get']['responses']['400']
        if openapi_version == '2.0':
            assert response['schema'] == schema_ref
        else:
            assert (response['content']['application/json']['schema'] ==
                    schema_ref)

        assert paths['/test/']['get']['responses']['404'] == response_ref
Exemplo n.º 24
0
    def test_blueprint_arguments_examples(self, app, schemas, openapi_version):
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        example = {'field': 12}
        examples = {'example 1': {'field': 12}, 'example 2': {'field': 42}}

        @blp.route('/example')
        @blp.arguments(schemas.DocSchema, example=example)
        def func_example():
            """Dummy view func"""

        @blp.route('/examples')
        @blp.arguments(schemas.DocSchema, examples=examples)
        def func_examples():
            """Dummy view func"""

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        get = spec['paths']['/test/example']['get']
        assert (get['requestBody']['content']['application/json']['example'] ==
                example)
        get = spec['paths']['/test/examples']['get']
        assert (get['requestBody']['content']['application/json']['examples']
                == examples)
Exemplo n.º 25
0
    def test_blueprint_route_parameters(self, app, openapi_version):
        """Check path parameters docs are merged with auto docs"""
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        @blp.route('/<int:item_id>',
                   parameters=[
                       'TestParameter',
                       {
                           'name': 'item_id',
                           'in': 'path',
                           'description': 'Item ID'
                       },
                   ])
        def get(item_id):
            pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        params = spec['paths']['/test/{item_id}']['parameters']
        assert len(params) == 2
        assert params[0] == build_ref(api.spec, 'parameter', 'TestParameter')
        assert params[1]['description'] == 'Item ID'
        if openapi_version == '2.0':
            assert params[1]['type'] == 'integer'
        else:
            assert params[1]['schema']['type'] == 'integer'
Exemplo n.º 26
0
    def test_blueprint_multiple_routes_per_view(self, app, as_method_view):
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        if as_method_view:
            # Blueprint.route ensures a different endpoint is used for each
            # route. Otherwise, this would break in Blueprint.route when
            # calling as_view for the second time with the same endpoint.
            @blp.route('/route_1')
            @blp.route('/route_2')
            class Resource(MethodView):
                def get(self):
                    pass

        else:

            @blp.route('/route_1')
            @blp.route('/route_2')
            def func():
                pass

        api.register_blueprint(blp)
        paths = api.spec.to_dict()['paths']

        assert 'get' in paths['/test/route_1']
        assert 'get' in paths['/test/route_2']
Exemplo n.º 27
0
    def test_blueprint_enforce_method_order(self, app, http_methods):
        api = Api(app)

        class MyBlueprint(Blueprint):
            HTTP_METHODS = http_methods

        blp = MyBlueprint('test', __name__, url_prefix='/test')

        @blp.route('/')
        class Resource(MethodView):
            def post(self):
                pass

            def put(self):
                pass

            def options(self):
                pass

            def patch(self):
                pass

            def head(self):
                pass

            def delete(self):
                pass

            def get(self):
                pass

        api.register_blueprint(blp)
        methods = list(api.spec.to_dict()['paths']['/test/'].keys())
        assert methods == [m.lower() for m in http_methods]
Exemplo n.º 28
0
    def test_blueprint_arguments_content_type(self, app, schemas, location,
                                              content_type, openapi_version):
        app.config['OPENAPI_VERSION'] = openapi_version
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')
        content_type = content_type or REQUEST_BODY_CONTENT_TYPE[location]

        @blp.route('/')
        @blp.arguments(schemas.DocSchema,
                       location=location,
                       content_type=content_type)
        def func():
            """Dummy view func"""

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        get = spec['paths']['/test/']['get']
        if openapi_version == '3.0.2':
            assert len(get['requestBody']['content']) == 1
            assert content_type in get['requestBody']['content']
        else:
            if content_type != 'application/json':
                assert get['consumes'] == [content_type]
            else:
                assert 'consumes' not in get
Exemplo n.º 29
0
    def test_blueprint_response_status_code_cast_to_string(
            self, app, status_code):
        api = Api(app)
        blp = Blueprint('test', __name__, url_prefix='/test')

        # This is a dummy example. In real-life, use 'description' parameter.
        doc_desc = {'description': 'Description'}

        class ItemSchema(ma.Schema):
            test = ma.fields.Int()

        @blp.route('/')
        class Resource(MethodView):

            # When documenting a response, @blp.doc MUST use the same type
            # to express the status code as the one used in @blp.response.
            # (Default is 200 expressed as int.)
            @blp.doc(**{'responses': {status_code: doc_desc}})
            @blp.arguments(ItemSchema)
            @blp.response(status_code, ItemSchema)
            def get(self):
                pass

        api.register_blueprint(blp)
        spec = api.spec.to_dict()
        resp = spec['paths']['/test/']['get']['responses']['200']
        assert resp['description'] == 'Description'
        assert 'schema' in resp['content']['application/json']
Exemplo n.º 30
0
def create_app(test_config=None):
    # create and configure the app
    app = Flask(__name__, instance_relative_config=True)

    app.config.from_object("lsgraph.config")

    if test_config is None:
        # load the instance config, if it exists, when not testing
        app.config.from_pyfile("config.py", silent=True)
    else:
        # load the test config if passed in
        app.config.from_mapping(test_config)

    if app.config["ENV"] == "production":
        assert app.config[
            "SECRET_KEY"] != "DEVELOPMENT", "Secret key must be changed"

    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    from . import models

    models.db.init_app(app)
    migrate = Migrate(app, models.db)
    ext_celery.init_app(app)

    api = Api(
        app,
        spec_kwargs={
            "security": [{
                "APIKey": [],
                "AuthToken": []
            }],
            "servers": [{
                "url": "https://www.learnershape.com",
                "description": "Learnershape hosted service",
            }],
        },
    )

    api.spec.components.security_scheme("APIKey", {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
    })
    api.spec.components.security_scheme("AuthToken", {
        "type": "apiKey",
        "in": "header",
        "name": "X-Auth-Token"
    })

    from . import api_v1

    api.register_blueprint(api_v1.api)

    return app