def create_app(): Flask.response_class = JsonResponse app = Flask(__name__) app.config['CLASSIFIER_BASE_PATH'] = app.instance_path app.config.from_object('inspire_classifier.config') docs = FlaskApiSpec(app) @app.route("/api/health") def date(): """Basic endpoint that returns the date, used to check if everything is up and working.""" now = datetime.datetime.now() return jsonify(now) docs.register(date) @app.route("/api/predict/coreness", methods=["POST"]) @use_kwargs({ 'title': fields.Str(required=True), 'abstract': fields.Str(required=True) }) @marshal_with(serializers.ClassifierOutputSerializer) def core_classifier(**kwargs): """Endpoint for the CORE classifier.""" return predict_coreness(kwargs['title'], kwargs['abstract']) docs.register(core_classifier) return app @app.errorhandler(404) def page_not_found(e): return {"errors": [str(e)]}, 404
def build_routes(app): """Register routes to given app instance.""" app.config.update({ 'APISPEC_SPEC': APISpec( title=SERVICE_NAME, openapi_version=OPENAPI_VERSION, version=API_VERSION, plugins=[MarshmallowPlugin()], ), 'APISPEC_SWAGGER_URL': API_SPEC_URL, }) app.register_blueprint(cache_blueprint) app.register_blueprint(dataset_blueprint) swaggerui_blueprint = get_swaggerui_blueprint( SWAGGER_URL, API_SPEC_URL, config={'app_name': 'Renku Service'}) app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL) docs = FlaskApiSpec(app) docs.register(upload_file_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(list_uploaded_files_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(project_clone, blueprint=CACHE_BLUEPRINT_TAG) docs.register(list_projects_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(create_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(add_file_to_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(list_datasets_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(list_dataset_files_view, blueprint=DATASET_BLUEPRINT_TAG)
def register_blueprints(app: Flask): """Register Flask blueprints.""" app.config.update({"APISPEC_SPEC": APISPEC_SPEC}) docs = FlaskApiSpec(app) from blueprints.images import images_bp from blueprints.images import routes as image_routes image_routes.set_routes(app, images_bp, docs)
def register_blueprints(app: Flask): """ Register Flask blueprints. """ app.config.update({"APISPEC_SPEC": APISPEC_SPEC}) docs = FlaskApiSpec(app) # example blueprint from blueprints.example import example_bp from blueprints.example import routes as example_routes example_routes.set_routes(app, example_bp, docs)
def init_app(app): """Register API resources on the provided Flask application.""" def register(path, resource): app.add_url_rule(path, view_func=resource.as_view(resource.__name__)) with warnings.catch_warnings(): warnings.simplefilter("ignore") docs.register(resource, endpoint=resource.__name__) docs = FlaskApiSpec(app) register("/models", Models) register("/models/<int:id>", IndvModel)
def create_app(): app = Flask(__name__) app.config['PROPAGATE_EXCEPTIONS'] = True docs = FlaskApiSpec(app) app.add_url_rule('/data/<path:folder>/chunks', view_func=DataResource.as_view('DataResource')) docs.register(DataResource, endpoint='DataResource') app.add_url_rule('/status', view_func=Status.as_view('Status')) docs.register(Status, endpoint='Status') return app
def __init__(self, app): app.config.update({ 'APISPEC_SPEC': APISpec( title='KodeSmil Flask', version='v1', openapi_version="3.0.2", info=dict(description='KodeSmil microservices API'), plugins=[MarshmallowPlugin()], ), 'APISPEC_SWAGGER_UI_URL': '/docs/', }) self.docs = FlaskApiSpec(app) self._initialize()
def register_blueprints(app): app.register_blueprint(blueprints_blueprint, url_prefix=API_BASE_URL + 'blueprints') app.register_blueprint(jobs_blueprint, url_prefix=API_BASE_URL + 'jobs') app.register_blueprint(operations_blueprint, url_prefix=API_BASE_URL + 'operations') # Swagger docs = FlaskApiSpec(app) from .jobs.routes import get_job docs.register(get_job, blueprint="jobs")
def __init__(self, app, url, **config): app.config.update({ 'APISPEC_SPEC': APISpec(**config, plugins=[MarshmallowPlugin()], **self.__default_security_schema()), 'APISPEC_SWAGGER_UI_URL': url, 'APISPEC_SWAGGER_URL': url + '.json' }) self.app = app self.doc = FlaskApiSpec(app) self.__register_security_schema()
def build_routes(app): """Register routes to given app instance.""" app.config.update({ "APISPEC_SPEC": APISpec( title=SERVICE_NAME, openapi_version=OPENAPI_VERSION, version=API_VERSION, plugins=[MarshmallowPlugin()], ), "APISPEC_SWAGGER_URL": API_SPEC_URL, }) app.register_blueprint(cache_blueprint) app.register_blueprint(dataset_blueprint) app.register_blueprint(jobs_blueprint) app.register_blueprint(templates_blueprint) app.register_blueprint(version_blueprint) swaggerui_blueprint = get_swaggerui_blueprint( SWAGGER_URL, API_SPEC_URL, config={"app_name": "Renku Service"}) app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL) docs = FlaskApiSpec(app) docs.register(version, blueprint=VERSION_BLUEPRINT_TAG) docs.register(list_uploaded_files_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(upload_file_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(project_clone_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(list_projects_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(migrate_project_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(migration_check_project_view, blueprint=CACHE_BLUEPRINT_TAG) docs.register(list_datasets_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(list_dataset_files_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(add_file_to_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(create_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(import_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(edit_dataset_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(unlink_file_view, blueprint=DATASET_BLUEPRINT_TAG) docs.register(list_jobs, blueprint=JOBS_BLUEPRINT_TAG) docs.register(read_manifest_from_template, blueprint=TEMPLATES_BLUEPRINT_TAG) docs.register(create_project_from_template, blueprint=TEMPLATES_BLUEPRINT_TAG)
def _register_openapi(app): from ursh.schemas import TokenSchema, URLSchema with app.app_context(): spec = APISpec( openapi_version='3.0.2', title='ursh - URL Shortener', version='2.0', plugins=( FlaskPlugin(), MarshmallowPlugin(), ), ) spec.components.schema('Token', schema=TokenSchema) spec.components.schema('URL', schema=URLSchema) app.config['APISPEC_SPEC'] = spec apispec = FlaskApiSpec(app) apispec.register_existing_resources()
def create_app(env_name): app = Flask(__name__) app.config.from_object(app_config[env_name]) bcrypt.init_app(app) db.init_app(app) app.register_blueprint(user_blueprint, url_prefix='/garden/v1/users') app.register_blueprint(device_blueprint, url_prefix='/garden/v1/devices') docs = FlaskApiSpec(app) docs.register(devices.create, blueprint=device_blueprint) @app.route('/', methods=['GET']) def index(): return 'blah blah blah' return app
def __init__(self): app = Flask(__name__) add_flask_profiler(app) with app.app_context(): log.info( f"starting {settings.APP_NAME} server on {settings.API.SERVER}" ) CORS(app) self.app = app self.docs = FlaskApiSpec(app) self.lineProfiler = line_profiler() log.info( f"starting {settings.APP_NAME} mongo connector {settings.MONGO}") self.mongo = MongoConnector(settings.MONGO) log.info( f"starting {settings.APP_NAME} postgres connector {settings.POSTGRES}" ) self.postgres = Postgres(settings.POSTGRES) """ When worker fork's the parent process, the db engine & connection pool is included in that. But, the db connections should not be shared across processes, so we tell the engine to dispose of all existing connections, which will cause new ones to be opened in the child processes as needed. More info: https://docs.sqlalchemy.org/en/latest/core/pooling.html#using-connection-pools-with-multiprocessing """ with app.app_context(): self.postgres.get_engine().dispose() log.info( f"starting {settings.APP_NAME} redis connector {settings.REDIS}") self.redis = RedisConnector(settings.REDIS) log.info( f"starting {settings.APP_NAME} rq dashboard at http://" f"{settings.API.SERVER.url}:{settings.API.SERVER.port}{settings.API.VERSION}{settings.APP_NAME}/rq" ) app.config["RQ_DASHBOARD_REDIS_URL"] = settings.REDIS.url log.info(f"starting {settings.APP_NAME} redis job queues") self.redisJobQueues = RedisJobQueues(self.redis.get_redis_conn()) log.info(f"starting {settings.APP_NAME} redis job queues") self.redisJobQueues = RedisJobQueues(self.redis.get_redis_conn()) log.info( f"started {settings.APP_NAME} server on {settings.API.SERVER}")
def create_app(): app = Flask(__name__) app.config.from_object('config.Config') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config.update({ 'APISPEC_TITLE': 'Pylerplate', 'APISPEC_VERSION': 'Pylerplate', 'APISPEC_SWAGGER_URL': '/swagger/', 'APISPEC_SWAGGER_UI_URL': None, }) app.__docs__ = FlaskApiSpec(app) initialize_extensions(app) register_handlers(app) CORS(app) register_routes(app) return app
def init_app(app: Flask): global FileField docs = FlaskApiSpec(document_options=False) ma = MarshmallowPlugin() spec = APISpec( title=app.config.get("APISPEC_TITLE", "API Docs"), version=app.config.get("APISPEC_VERSION", "v1"), openapi_version=app.config.get("APISPEC_OAS_VERSION", "2.0"), plugins=[ma], securityDefinitions=app.config.get("APISPEC_SECURITY_DEFINITIONS", {}), ) @ma.map_to_openapi_type("file", None) class FileField(fields.Raw): pass app.config.update({"APISPEC_SPEC": spec}) docs.init_app(app) app.extensions["apispec"] = docs
def register_spec(app): app.config.update({ "APISPEC_SPEC": APISpec( title="Flask VRFs API", version="0.0.1", openapi_version="2.0", plugins=[MarshmallowPlugin()], ), "APISPEC_SWAGGER_URL": "/api/docs/apispec.json", "APISPEC_SWAGGER_UI_URL": "/api/docs", }) docs = FlaskApiSpec(app) from app.api.vrf.views import Vrf docs.register(Vrf, blueprint="api") from app.api.vrfs.views import Vrfs docs.register(Vrfs, blueprint="api")
def main(): app = Flask('MySchemaApp') docs = FlaskApiSpec(app) parser = argparse.ArgumentParser() parser.add_argument('-x', dest='xvalue', type=str, help='the x value', default='hello') parser.add_argument('-y', dest='yvalue', type=int, help='the y value', default=10) MySchema = parser_to_schema(parser, 'MySchema') my_schema = MySchema() @docs.register @app.route('/api') @use_kwargs(MySchema, locations=['query']) def handler(**kwargs): obj = my_schema.load(kwargs) print('recv args: {}'.format(kwargs)) print('recv obj: {}'.format(obj)) return my_schema.dump(obj) return app
import os from flask import Flask from flask_mongoengine import MongoEngine from flask_restful import Api from apispec import APISpec from apispec.ext.marshmallow import MarshmallowPlugin from flask_apispec import FlaskApiSpec from celery import Celery from .tasks import make_celery from .config import configs env = os.environ.get("FLASK_ENV", "development") db = MongoEngine() rest_api = Api() docs = FlaskApiSpec() celery = Celery( __name__, broker=configs[env].CELERY_BROKER_URL, backend=configs[env].CELERY_RESULT_BACKEND ) def create_app(test_config=None): app = Flask(__name__, instance_relative_config=True) app.config.from_object(configs[env]) app.config.update({ 'APISPEC_SPEC': APISpec( title='openforms', version='v1', openapi_version='2.0', plugins=[MarshmallowPlugin()], ), 'APISPEC_SWAGGER_URL': '/swagger/',
from . import settings from .middlewares import ( HTTPMethodOverrideMiddleware, ) from .extensions import ( Webpack, ) from . import commands db = SQLAlchemy() ds_client = None cache = Cache() user_datastore = None security = None mail = Mail() babel = Babel() marshmallow = Marshmallow() api_spec = FlaskApiSpec() def create_app(config_object=settings): app = Flask(__name__) app.config.from_object(config_object) global ds_client ds_client = datastore.Client() register_extensions(app) register_middlewares(app) register_commands(app) init_security(app) register_blueprints(app)
def create_app( name: str = __name__, mode: ServerModes = ServerModes.NORMAL, options: Optional[Dict[str, bool]] = None, ) -> Flask: """ Create the server istance for Flask application """ if PRODUCTION and TESTING and not FORCE_PRODUCTION_TESTS: # pragma: no cover print_and_exit("Unable to execute tests in production") # TERM is not catched by Flask # https://github.com/docker/compose/issues/4199#issuecomment-426109482 # signal.signal(signal.SIGTERM, teardown_handler) # SIGINT is registered as STOPSIGNAL in Dockerfile signal.signal(signal.SIGINT, teardown_handler) # Flask app instance # template_folder = template dir for output in HTML microservice = Flask( name, template_folder=os.path.join(ABS_RESTAPI_PATH, "templates") ) # CORS if not PRODUCTION: cors = CORS( allow_headers=[ "Content-Type", "Authorization", "X-Requested-With", "x-upload-content-length", "x-upload-content-type", "content-range", ], supports_credentials=["true"], methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], ) cors.init_app(microservice) log.debug("CORS Injected") # Flask configuration from config file microservice.config.from_object(config) log.debug("Flask app configured") if PRODUCTION: log.info("Production server mode is ON") endpoints_loader = EndpointsLoader() mem.configuration = endpoints_loader.load_configuration() mem.initializer = Meta.get_class("initialization", "Initializer") if not mem.initializer: # pragma: no cover print_and_exit("Invalid Initializer class") mem.customizer = Meta.get_instance("customization", "Customizer") if not mem.customizer: # pragma: no cover print_and_exit("Invalid Customizer class") if not isinstance(mem.customizer, BaseCustomizer): # pragma: no cover print_and_exit("Invalid Customizer class, it should inherit BaseCustomizer") Connector.init_app(app=microservice, worker_mode=(mode == ServerModes.WORKER)) # Initialize reading of all files mem.geo_reader = geolite2.reader() # when to close?? # geolite2.close() if mode == ServerModes.INIT: Connector.project_init(options=options) if mode == ServerModes.DESTROY: Connector.project_clean() # Restful plugin with endpoint mapping (skipped in INIT|DESTROY|WORKER modes) if mode == ServerModes.NORMAL: logging.getLogger("werkzeug").setLevel(logging.ERROR) # ignore warning messages from apispec warnings.filterwarnings( "ignore", message="Multiple schemas resolved to the name " ) mem.cache = Cache.get_instance(microservice) endpoints_loader.load_endpoints() mem.authenticated_endpoints = endpoints_loader.authenticated_endpoints mem.private_endpoints = endpoints_loader.private_endpoints # Triggering automatic mapping of REST endpoints rest_api = Api(catch_all_404s=True) for endpoint in endpoints_loader.endpoints: # Create the restful resource with it; # this method is from RESTful plugin rest_api.add_resource(endpoint.cls, *endpoint.uris) # HERE all endpoints will be registered by using FlaskRestful rest_api.init_app(microservice) # APISpec configuration api_url = get_backend_url() scheme, host = api_url.rstrip("/").split("://") spec = APISpec( title=get_project_configuration( "project.title", default="Your application name" ), version=get_project_configuration("project.version", default="0.0.1"), openapi_version="2.0", # OpenApi 3 not working with FlaskApiSpec # -> Duplicate parameter with name body and location body # https://github.com/jmcarp/flask-apispec/issues/170 # Find other warning like this by searching: # **FASTAPI** # openapi_version="3.0.2", plugins=[MarshmallowPlugin()], host=host, schemes=[scheme], tags=endpoints_loader.tags, ) # OpenAPI 3 changed the definition of the security level. # Some changes needed here? api_key_scheme = {"type": "apiKey", "in": "header", "name": "Authorization"} spec.components.security_scheme("Bearer", api_key_scheme) microservice.config.update( { "APISPEC_SPEC": spec, # 'APISPEC_SWAGGER_URL': '/api/swagger', "APISPEC_SWAGGER_URL": None, # 'APISPEC_SWAGGER_UI_URL': '/api/swagger-ui', # Disable Swagger-UI "APISPEC_SWAGGER_UI_URL": None, } ) mem.docs = FlaskApiSpec(microservice) # Clean app routes ignore_verbs = {"HEAD", "OPTIONS"} for rule in microservice.url_map.iter_rules(): endpoint = microservice.view_functions[rule.endpoint] if not hasattr(endpoint, "view_class"): continue newmethods = ignore_verbs.copy() rulename = str(rule) for verb in rule.methods - ignore_verbs: method = verb.lower() if method in endpoints_loader.uri2methods[rulename]: # remove from flask mapping # to allow 405 response newmethods.add(verb) rule.methods = newmethods # Register swagger. Note: after method mapping cleaning with microservice.app_context(): for endpoint in endpoints_loader.endpoints: try: mem.docs.register(endpoint.cls) except TypeError as e: # pragma: no cover print(e) log.error("Cannot register {}: {}", endpoint.cls.__name__, e) # marshmallow errors handler microservice.register_error_handler(422, handle_marshmallow_errors) # Logging responses microservice.after_request(handle_response) if SENTRY_URL is not None: # pragma: no cover if PRODUCTION: sentry_sdk.init( dsn=SENTRY_URL, # already catched by handle_marshmallow_errors ignore_errors=[werkzeug.exceptions.UnprocessableEntity], integrations=[FlaskIntegration()], ) log.info("Enabled Sentry {}", SENTRY_URL) else: # Could be enabled in print mode # sentry_sdk.init(transport=print) log.info("Skipping Sentry, only enabled in PRODUCTION mode") log.info("Boot completed") return microservice
import marshmallow from flask_apispec import FlaskApiSpec, marshal_with, MethodResource class Api: """ Holds a version of the API with documentation and allows routes to register. """ def __init__(self, prefix=""): self.prefix = prefix self._deferred_routes = [] def register(self, path, aMethodResource): self._deferred_routes.append(( "/{0}/{1}".format(self.prefix, path.lstrip("/")), aMethodResource.as_view(name=aMethodResource.__module__ + "_" + aMethodResource.__name__.lower()), )) def init_app(self, app): for deferred in self._deferred_routes: app.add_url_rule(deferred[0], view_func=deferred[1]) specs.register(deferred[1]) specs = FlaskApiSpec() api_v1 = Api(prefix="v0.1")
spec.register(cls) return cls return wrapper app.config.update({ 'APISPEC_SPEC': APISpec( title='Flask-apispec user API', version='1.0', plugins=(MarshmallowPlugin(), ), ), 'APISPEC_SWAGGER_URL': '/swagger.json', }) spec = FlaskApiSpec(app) class UserSchema(Schema): id = ma_fields.Integer() name = ma_fields.String() class Meta: strict = True class LoginSchema(Schema): logged_user = ma_fields.String() @route(app, spec, '/user')
APP.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db' APP.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False APP.config.update({ 'APISPEC_SPEC': APISpec( title='Awesome Project', version='v1', plugins=[MarshmallowPlugin()], openapi_version='2.0.0' ), 'APISPEC_SWAGGER_URL': '/swagger/', # URI to access API Doc JSON 'APISPEC_SWAGGER_UI_URL': '/swagger-ui/' # URI to access UI of API Doc }) API = Api(APP) db.init_app(APP) DOCS = FlaskApiSpec(APP) @APP.before_first_request def create_table(): '''Takes in a number n, returns the square of n''' db.create_all() class AwesomeResponseSchema(Schema): '''Takes in a number n, returns the square of n''' message = fields.Str(default='Success') class AwesomeRequestSchema(Schema):
""" Reference: https://github.com/tiangolo/full-stack-flask-couchdb/tree/master/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app/api/api_v1 """ from apispec import APISpec from apispec.ext.marshmallow import MarshmallowPlugin from flask.app import Flask from flask_apispec import FlaskApiSpec docs = FlaskApiSpec(document_options=False) def init_app(app: Flask): app.config.update( { "APISPEC_SPEC": APISpec( title=app.config.get("APISPEC_TITLE", "API Docs"), version=app.config.get("APISPEC_VERSION", "v1"), openapi_version=app.config.get("APISPEC_OAS_VERSION", "2.0"), plugins=[MarshmallowPlugin()], securityDefinitions=app.config.get("APISPEC_SECURITY_DEFINITIONS", {}), ), } ) docs.init_app(app)
def init_swagger(app): docs = FlaskApiSpec(app) docs.register(UsersController) docs.register(UserController) return docs
api.add_resource( filings.FilingsView, '/committee/<committee_id>/filings/', '/candidate/<candidate_id>/filings/', ) api.add_resource(filings.FilingsList, '/filings/') api.add_resource(download.DownloadView, '/download/<path:path>/') app.config.update({ 'APISPEC_SWAGGER_URL': None, 'APISPEC_SWAGGER_UI_URL': None, 'APISPEC_SPEC': spec.spec, }) apidoc = FlaskApiSpec(app) apidoc.register(search.CandidateNameSearch, blueprint='v1') apidoc.register(search.CommitteeNameSearch, blueprint='v1') apidoc.register(candidates.CandidateView, blueprint='v1') apidoc.register(candidates.CandidateList, blueprint='v1') apidoc.register(candidates.CandidateSearch, blueprint='v1') apidoc.register(candidates.CandidateHistoryView, blueprint='v1') apidoc.register(committees.CommitteeView, blueprint='v1') apidoc.register(committees.CommitteeList, blueprint='v1') apidoc.register(committees.CommitteeHistoryView, blueprint='v1') apidoc.register(reports.ReportsView, blueprint='v1') apidoc.register(totals.TotalsView, blueprint='v1') apidoc.register(sched_a.ScheduleAView, blueprint='v1') apidoc.register(sched_b.ScheduleBView, blueprint='v1') apidoc.register(sched_e.ScheduleEView, blueprint='v1')
from flask import Flask, redirect from flask_apispec import FlaskApiSpec from .skin_detect import * import os from flask import Flask, flash, request, redirect, url_for from werkzeug.utils import secure_filename import cv2 from flask import jsonify app = Flask(__name__) docs = FlaskApiSpec(app) app.add_url_rule('/skin_detect', view_func=ImageEndPoint.as_view('skin_detect')) docs.register(ImageEndPoint, endpoint="skin_detect") import skin_detector UPLOAD_FOLDER = './uploads' ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
APISpec( title='Test Chassis Service', version='1.0.0-b1', openapi_version="2.0", # openapi_version="3.0.2", plugins=[MarshmallowPlugin()], info=dict(description= "Handles common resources shared by the entire architecture", license={ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" })), 'APISPEC_SWAGGER_URL': '/swagger/', }) docs = FlaskApiSpec(flask_test_app) docs.register(TestApiList) docs.register(TestApi) docs.register(TestCountryList) docs.register(TestCountryApi) flask_test_app.register_error_handler(422, validation_error_handler) @flask_test_app.errorhandler(InvalidTokenError) def unauthorized(error): flask_test_app.logger.error("Authorization error: %s", error) return { "message": "You are not authorized to perform this request. " "Ensure you have a valid credentials before trying again"
def fd_app(cache_dir): """ API app for FreeDiscovery """ if not os.path.exists(cache_dir): raise ValueError('Cache_dir {} does not exist!'.format(cache_dir)) app = Flask('freediscovery_api') app.url_map.strict_slashes = False app.config.update({ 'APISPEC_SPEC': APISpec(title='FreeDiscovery', version='v0', plugins=['apispec.ext.marshmallow']), 'APISPEC_SWAGGER_URL': '/openapi-specs.json', 'APISPEC_SWAGGER_UI_URL': '/swagger-ui.html' }) app.test_client_class = TestClient docs = FlaskApiSpec(app) #app.config['DEBUG'] = False ## Actually setup the Api resource routing here for resource_el, url in [ (ExampleDatasetApi, "/example-dataset/<name>"), (FeaturesApi, "/feature-extraction"), (FeaturesApiElement, '/feature-extraction/<dsid>'), (FeaturesApiElementMappingNested, '/feature-extraction/<dsid>/id-mapping'), (EmailParserApi, "/email-parser"), (EmailParserApiElement, '/email-parser/<dsid>'), (EmailParserApiElementIndex, '/email-parser/<dsid>/index'), (ModelsApi, '/categorization/'), (ModelsApiElement, '/categorization/<mid>'), (ModelsApiPredict, '/categorization/<mid>/predict'), (LsiApi, '/lsi/'), (LsiApiElement, '/lsi/<mid>'), (KmeanClusteringApi, '/clustering/k-mean/'), (BirchClusteringApi, '/clustering/birch'), (WardHCClusteringApi, '/clustering/ward_hc'), (DBSCANClusteringApi, '/clustering/dbscan'), (ClusteringApiElement, '/clustering/<method>/<mid>'), (DupDetectionApi, '/duplicate-detection/'), (DupDetectionApiElement, '/duplicate-detection/<mid>'), (MetricsCategorizationApiElement, '/metrics/categorization'), (MetricsClusteringApiElement, '/metrics/clustering'), (MetricsDupDetectionApiElement, '/metrics/duplicate-detection'), (EmailThreadingApi, '/email-threading/'), (EmailThreadingApiElement, '/email-threading/<mid>'), (SearchApi, '/search/'), (CustomStopWordsApi, '/stop-words/'), (CustomStopWordsLoadApi, '/stop-words/<name>'), #(CatchAll , "/<url>") ]: # monkeypatching, not great resource_el._cache_dir = cache_dir resource_el.methods = ['GET', 'POST', 'DELETE'] #api.add_resource(resource_el, '/api/v0' + url, strict_slashes=False) ressource_name = resource_el.__name__ app.add_url_rule('/api/v0' + url, view_func=resource_el.as_view(ressource_name)) if ressource_name not in ["EmailThreadingApi"]: # the above two cases fail due to recursion limit docs.register(resource_el, endpoint=ressource_name) @app.errorhandler(500) def handle_error(error): #response = jsonify(error.to_dict()) response = jsonify({'messages': str(error)}) response.status_code = 500 return response @app.errorhandler(404) def handle_404_error(error): response = jsonify({"messages": str(error)}) response.status_code = 404 return response @app.errorhandler(400) def handle_400_error(error): response = jsonify({"messages": str(error)}) response.status_code = 400 return response @app.errorhandler(422) def handle_422_error(error): # marshmallow error response = jsonify(error.data) response.status_code = 422 return response return app
def create_app( name: str = __name__, mode: ServerModes = ServerModes.NORMAL, options: Optional[Dict[str, bool]] = None, ) -> Flask: """Create the server istance for Flask application""" if PRODUCTION and TESTING and not FORCE_PRODUCTION_TESTS: # pragma: no cover print_and_exit("Unable to execute tests in production") # TERM is not catched by Flask # https://github.com/docker/compose/issues/4199#issuecomment-426109482 # signal.signal(signal.SIGTERM, teardown_handler) # SIGINT is registered as STOPSIGNAL in Dockerfile signal.signal(signal.SIGINT, teardown_handler) # Flask app instance # template_folder = template dir for output in HTML flask_app = Flask(name, template_folder=str(ABS_RESTAPI_PATH.joinpath("templates"))) # CORS if not PRODUCTION: if TESTING: cors_origin = "*" else: # pragma: no cover cors_origin = get_frontend_url() # Beware, this only works because get_frontend_url never append a port cors_origin += ":*" CORS( flask_app, allow_headers=[ "Content-Type", "Authorization", "X-Requested-With", "x-upload-content-length", "x-upload-content-type", "content-range", ], supports_credentials=["true"], methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], resources={r"*": {"origins": cors_origin}}, ) log.debug("CORS Enabled") # Flask configuration from config file flask_app.config.from_object(config) flask_app.json_encoder = ExtendedJSONEncoder # Used to force flask to avoid json sorting and ensure that # the output to reflect the order of field in the Marshmallow schema flask_app.config["JSON_SORT_KEYS"] = False log.debug("Flask app configured") if PRODUCTION: log.info("Production server mode is ON") endpoints_loader = EndpointsLoader() if HOST_TYPE == DOCS: # pragma: no cover log.critical("Creating mocked configuration") mem.configuration = {} log.critical("Loading Mocked Initializer and Customizer classes") from restapi.mocks import Customizer, Initializer mem.initializer = Initializer mem.customizer = Customizer() else: mem.configuration = endpoints_loader.load_configuration() mem.initializer = Meta.get_class("initialization", "Initializer") if not mem.initializer: # pragma: no cover print_and_exit("Invalid Initializer class") customizer = Meta.get_class("customization", "Customizer") if not customizer: # pragma: no cover print_and_exit("Invalid Customizer class") mem.customizer = customizer() if not isinstance(mem.customizer, BaseCustomizer): # pragma: no cover print_and_exit("Invalid Customizer class, it should inherit BaseCustomizer") Connector.init_app(app=flask_app, worker_mode=(mode == ServerModes.WORKER)) # Initialize reading of all files mem.geo_reader = geolite2.reader() # when to close?? # geolite2.close() if mode == ServerModes.INIT: Connector.project_init(options=options) if mode == ServerModes.DESTROY: Connector.project_clean() # Restful plugin with endpoint mapping (skipped in INIT|DESTROY|WORKER modes) if mode == ServerModes.NORMAL: logging.getLogger("werkzeug").setLevel(logging.ERROR) # warnings levels: # default # Warn once per call location # error # Convert to exceptions # always # Warn every time # module # Warn once per calling module # once # Warn once per Python process # ignore # Never warn # Types of warnings: # Warning: This is the base class of all warning category classes # UserWarning: The default category for warn(). # DeprecationWarning: Base category for warnings about deprecated features when # those warnings are intended for other Python developers # SyntaxWarning: Base category for warnings about dubious syntactic features. # RuntimeWarning: Base category for warnings about dubious runtime features. # FutureWarning: Base category for warnings about deprecated features when those # warnings are intended for end users # PendingDeprecationWarning: Base category for warnings about features that will # be deprecated in the future (ignored by default). # ImportWarning: Base category for warnings triggered during the process of # importing a module # UnicodeWarning: Base category for warnings related to Unicode. # BytesWarning: Base category for warnings related to bytes and bytearray. # ResourceWarning: Base category for warnings related to resource usage if TESTING: warnings.simplefilter("always", Warning) warnings.simplefilter("error", UserWarning) warnings.simplefilter("error", DeprecationWarning) warnings.simplefilter("error", SyntaxWarning) warnings.simplefilter("error", RuntimeWarning) warnings.simplefilter("error", FutureWarning) # warnings about features that will be deprecated in the future warnings.simplefilter("default", PendingDeprecationWarning) warnings.simplefilter("error", ImportWarning) warnings.simplefilter("error", UnicodeWarning) warnings.simplefilter("error", BytesWarning) # Can't set this an error due to false positives with downloads # a lot of issues like: https://github.com/pallets/flask/issues/2468 warnings.simplefilter("always", ResourceWarning) warnings.simplefilter("default", Neo4jExperimentalWarning) # Remove me in a near future, this is due to hypothesis with pytest 7 # https://github.com/HypothesisWorks/hypothesis/issues/3222 warnings.filterwarnings( "ignore", message="A private pytest class or function was used." ) elif PRODUCTION: # pragma: no cover warnings.simplefilter("ignore", Warning) warnings.simplefilter("always", UserWarning) warnings.simplefilter("default", DeprecationWarning) warnings.simplefilter("ignore", SyntaxWarning) warnings.simplefilter("ignore", RuntimeWarning) warnings.simplefilter("ignore", FutureWarning) warnings.simplefilter("ignore", PendingDeprecationWarning) warnings.simplefilter("ignore", ImportWarning) warnings.simplefilter("ignore", UnicodeWarning) warnings.simplefilter("ignore", BytesWarning) warnings.simplefilter("ignore", ResourceWarning) # even if ignore it is raised once # because of the imports executed before setting this to ignore warnings.simplefilter("ignore", Neo4jExperimentalWarning) else: # pragma: no cover warnings.simplefilter("default", Warning) warnings.simplefilter("always", UserWarning) warnings.simplefilter("always", DeprecationWarning) warnings.simplefilter("default", SyntaxWarning) warnings.simplefilter("default", RuntimeWarning) warnings.simplefilter("always", FutureWarning) warnings.simplefilter("default", PendingDeprecationWarning) warnings.simplefilter("default", ImportWarning) warnings.simplefilter("default", UnicodeWarning) warnings.simplefilter("default", BytesWarning) warnings.simplefilter("always", ResourceWarning) # even if ignore it is raised once # because of the imports executed before setting this to ignore warnings.simplefilter("ignore", Neo4jExperimentalWarning) # ignore warning messages from apispec warnings.filterwarnings( "ignore", message="Multiple schemas resolved to the name " ) # ignore warning messages on flask socket after teardown warnings.filterwarnings("ignore", message="unclosed <socket.socket") # from flask_caching 1.10.1 with python 3.10 on core tests... # try to remove this once upgraded flask_caching in a near future warnings.filterwarnings( "ignore", message="_SixMetaPathImporter.find_spec", ) # Raised from sentry_sdk 1.5.11 with python 3.10 events warnings.filterwarnings( "ignore", message="SelectableGroups dict interface is deprecated. Use select.", ) mem.cache = Cache.get_instance(flask_app) endpoints_loader.load_endpoints() mem.authenticated_endpoints = endpoints_loader.authenticated_endpoints mem.private_endpoints = endpoints_loader.private_endpoints for endpoint in endpoints_loader.endpoints: ename = endpoint.cls.__name__.lower() endpoint_view = endpoint.cls.as_view(ename) for url in endpoint.uris: flask_app.add_url_rule(url, view_func=endpoint_view) # APISpec configuration api_url = get_backend_url() scheme, host = api_url.rstrip("/").split("://") spec = APISpec( title=get_project_configuration( "project.title", default="Your application name" ), version=get_project_configuration("project.version", default="0.0.1"), openapi_version="2.0", # OpenApi 3 not working with FlaskApiSpec # -> Duplicate parameter with name body and location body # https://github.com/jmcarp/flask-apispec/issues/170 # Find other warning like this by searching: # **FASTAPI** # openapi_version="3.0.2", plugins=[MarshmallowPlugin()], host=host, schemes=[scheme], tags=endpoints_loader.tags, ) # OpenAPI 3 changed the definition of the security level. # Some changes needed here? if Env.get_bool("AUTH_ENABLE"): api_key_scheme = {"type": "apiKey", "in": "header", "name": "Authorization"} spec.components.security_scheme("Bearer", api_key_scheme) flask_app.config.update( { "APISPEC_SPEC": spec, # 'APISPEC_SWAGGER_URL': '/api/swagger', "APISPEC_SWAGGER_URL": None, # 'APISPEC_SWAGGER_UI_URL': '/api/swagger-ui', # Disable Swagger-UI "APISPEC_SWAGGER_UI_URL": None, } ) mem.docs = FlaskApiSpec(flask_app) # Clean app routes ignore_verbs = {"HEAD", "OPTIONS"} for rule in flask_app.url_map.iter_rules(): view_function = flask_app.view_functions[rule.endpoint] if not hasattr(view_function, "view_class"): continue newmethods = ignore_verbs.copy() rulename = str(rule) if rule.methods: for verb in rule.methods - ignore_verbs: method = verb.lower() if method in endpoints_loader.uri2methods[rulename]: # remove from flask mapping # to allow 405 response newmethods.add(verb) rule.methods = newmethods # Register swagger. Note: after method mapping cleaning with flask_app.app_context(): for endpoint in endpoints_loader.endpoints: try: mem.docs.register(endpoint.cls) except TypeError as e: # pragma: no cover print(e) log.error("Cannot register {}: {}", endpoint.cls.__name__, e) # marshmallow errors handler # Can't get the typing to work with flask 2.1 flask_app.register_error_handler(422, handle_marshmallow_errors) # type: ignore flask_app.register_error_handler(400, handle_http_errors) # type: ignore flask_app.register_error_handler(404, handle_http_errors) # type: ignore flask_app.register_error_handler(405, handle_http_errors) # type: ignore flask_app.register_error_handler(500, handle_http_errors) # type: ignore # flask_app.before_request(inspect_request) # Logging responses # Can't get the typing to work with flask 2.1 flask_app.after_request(handle_response) # type: ignore if SENTRY_URL is not None: # pragma: no cover if PRODUCTION: sentry_sdk_init( dsn=SENTRY_URL, # already catched by handle_marshmallow_errors ignore_errors=[werkzeug.exceptions.UnprocessableEntity], integrations=[FlaskIntegration()], ) log.info("Enabled Sentry {}", SENTRY_URL) else: # Could be enabled in print mode # sentry_sdk_init(transport=print) log.info("Skipping Sentry, only enabled in PRODUCTION mode") log.info("Boot completed") if PRODUCTION and not TESTING and name == MAIN_SERVER_NAME: # pragma: no cover save_event_log( event=Events.server_startup, payload={"server": name}, user=None, target=None, ) return flask_app