class Config:
    db_connection_string: str
    db_name: str
    dev: bool
    postgres_connection_string: str
    redis_connection_string: str
    b2c_client_id: str = None
    b2c_client_secret: str = None
    b2c_tenant: str = None
    b2c_user_flow: str = None
    base_url: str = ""
    data_path: Path = None
    fake: bool = False
    fake_path: Path = None
    force_version: str = None
    host: str = None
    no_check_files: bool = False
    no_check_db: bool = False
    no_fetching: bool = False
    no_sentry: bool = False
    port: int = 9950
    proxy: str = None
    use_b2c: bool = False
    verbose: bool = False
    sentry_dsn: str = "https://9a2f8d1a3f7a431e873207a70ef3d44d:ca6db07b82934005beceae93560a6794@sentry.io/220532" async def startup_routes(app: aiohttp.web.Application):
    """Add routes to jobs API."""
    for routes in virtool.routes.ROUTES:
        app.add_routes(routes.jobs_api) async def startup_db(app: App):
    """
    An application ``on_startup`` callback that attaches an instance of
    :class:`~AsyncIOMotorClient` and the ``db_name`` to the Virtool ``app`` object. Also
    initializes collection indices.

    :param app: the app object
    :type app: :class:`aiohttp.aiohttp.web.Application`

    """
    logger.info("Connecting to MongoDB")

    dispatcher_interface = DispatcherClient(app["redis"])
    await get_scheduler_from_app(app).spawn(dispatcher_interface.run())

    app["db"] = await virtool.db.mongo.connect(
        app["config"], dispatcher_interface.enqueue_change
    )
    app["dispatcher_interface"] = dispatcher_interface async def startup_events(app: Application):
    events = create_events()

    loop = asyncio.get_event_loop()

    loop.add_signal_handler(signal.SIGINT, events["shutdown"].set)
    loop.add_signal_handler(signal.SIGTERM, events["shutdown"].set)

    app["events"] = events async def startup_executors(app: Application):
    """
    An application ``on_startup`` callback that initializes a
    :class:`~ThreadPoolExecutor` and attaches it to the ``app`` object.

    :param app: the application object

    """
    loop = asyncio.get_event_loop()

    thread_executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)

    loop.set_default_executor(thread_executor)

    async def run_in_thread(func, *args):
        return await loop.run_in_executor(thread_executor, func, *args)

    app["run_in_thread"] = run_in_thread
    app["executor"] = thread_executor

    process_executor = concurrent.futures.ProcessPoolExecutor()

    async def run_in_process(func, *args):
        return await loop.run_in_executor(process_executor, func, *args)

    app["run_in_process"] = run_in_process
    app["process_executor"] = process_executor async def startup_fake(app: Application):
    if app["config"].fake:
        app["fake"] = FakerWrapper()
        await populate(app) async def startup_fake_config(app: App):
    """
    If the ``fake`` config flag is set, patch the config so that the MongoDB and
    Postgres databases and the data directory are faked.

    :param app:

    """
    suffix = random_alphanumeric()

    if app["config"].fake:
        url = urlparse(app["config"].postgres_connection_string)

        base_connection_string = urlunparse((url.scheme, url.netloc, "", "", "", ""))

        name = f"fake_{suffix}"

        await create_test_database(base_connection_string, name)

        app["config"].db_name = f"fake-{suffix}"
        app["config"].data_path = create_fake_data_path()
        app["config"].postgres_connection_string = f"{base_connection_string}/{name}" async def startup_postgres(app: Application):
    """
    An application ``on_startup`` callback that attaches an instance of
    :class:`~AsyncConnection` to the Virtool ``app`` object.

    :param app: the app object

    """
    postgres_connection_string = app["config"].postgres_connection_string

    logger.info("Connecting to PostgreSQL")

    app["pg"] = await virtool.pg.utils.connect(postgres_connection_string) async def startup_redis(app: typing.Union[dict, Application]):
    redis_connection_string = app["config"].redis_connection_string
    logger.info("Connecting to Redis")
    redis = await virtool.redis.connect(redis_connection_string)

    scheduler = get_scheduler_from_app(app)
    await scheduler.spawn(periodically_ping_redis(redis))

    app["redis"] = redis async def startup_settings(app: typing.Union[dict, Application]):
    """
    Draws settings from the settings database collection and populates `app["settings"`.

    Performs migration of old settings style to `v3.3.0` if necessary.

    :param app: the app object

    """
    app["settings"] = await ensure(app["db"])
import asyncio
import aiojobs.aiohttp
import virtool.http.accept
import virtool.http.errors
import virtool.jobs.auth
from typing import Tuple
from aiohttp.web import Application, AppRunner
from virtool.config.cls import Config
from virtool.dev.fake import drop_fake_mongo, remove_fake_data_path
from virtool.jobs.routes import startup_routes
from virtool.process_utils import create_app_runner, wait_for_restart, wait_for_shutdown
from virtool.shutdown import drop_fake_postgres
from virtool.startup import (
    startup_db,
    startup_events,
    startup_executors,
    startup_fake,
    startup_fake_config,
    startup_postgres,
    startup_redis,
    startup_settings,
)
from virtool.types import App



async def create_app(config: Config):
    """Create the :class:`aiohttp.web.Application` for the jobs API process."""
    middlewares = [
        virtool.http.accept.middleware,
        virtool.jobs.auth.middleware,
        virtool.http.errors.middleware,
    ]

    app = Application(client_max_size=1024 ** 2 * 20, middlewares=middlewares)

    app["config"] = config
    app["mode"] = "jobs_api_server"

    aiojobs.aiohttp.setup(app)

    app.on_startup.extend(
        [
            startup_fake_config,
            startup_redis,
            startup_db,
            startup_postgres,

            startup_settings,