def test_get_api_base_url_works(self):

        # test the api_base_url feature, and that it can contain a path
        config = AppConfig()
        backend_port = find_available_port("localhost", 10000)
        config.update_server_config(
            app__flask_secret_key="secret",
            app__api_base_url=
            f"http://localhost:{backend_port}/additional/path",
            multi_dataset__dataroot=f"{PROJECT_ROOT}/example-dataset",
        )

        config.complete_config()

        with test_server(["-p", str(backend_port)],
                         app_config=config) as server:
            session = requests.Session()
            self.assertEqual(server, f"http://localhost:{backend_port}")
            response = session.get(
                f"{server}/additional/path/d/pbmc3k.h5ad/api/v0.2/config")
            self.assertEqual(response.status_code, 200)
            data_config = response.json()
            self.assertEqual(data_config["config"]["displayNames"]["dataset"],
                             "pbmc3k")

            # test the health check at the correct url
            response = session.get(f"{server}/additional/path/health")
            assert response.json()["status"] == "pass"
Exemple #2
0
def start_test_server(command_line_args=[], app_config=None, env=None):
    """
    Command line arguments can be passed in, as well as an app_config.
    This function is meant to be used like this, for example:

    with test_server(...) as server:
        r = requests.get(f"{server}/...")
        // check r

    where the server can be accessed within the context, and is terminated when
    the context is exited.
    The port is automatically set using find_available_port, unless passed in as a command line arg.
    The verbose flag is automatically set to True.
    If an app_config is provided, then this function writes a temporary
    yaml config file, which this server will read and parse.
    """

    command = ["cellxgene", "--no-upgrade-check", "launch", "--verbose"]
    if "-p" in command_line_args:
        port = int(command_line_args[command_line_args.index("-p") + 1])
    elif "--port" in command_line_args:
        port = int(command_line_args[command_line_args.index("--port") + 1])
    else:
        start = random.randint(DEFAULT_SERVER_PORT, 2**16 - 1)
        port = int(os.environ.get("CXG_SERVER_PORT", start))
        port = find_available_port("localhost", port)
        command += ["--port=%d" % port]

    command += command_line_args

    tempdir = None
    if app_config:
        tempdir = tempfile.TemporaryDirectory()
        config_file = os.path.join(tempdir.name, "config.yaml")
        app_config.write_config(config_file)
        command.extend(["-c", config_file])

    server = f"http://localhost:{port}"
    ps = Popen(command, env=env)

    for _ in range(10):
        try:
            requests.get(f"{server}/health")
            break
        except requests.exceptions.ConnectionError:
            time.sleep(1)

    if tempdir:
        tempdir.cleanup()

    return ps, server
Exemple #3
0
    def handle_app(self, context):
        self.validate_correct_type_of_configuration_attribute(
            "app__verbose", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__debug", bool)
        self.validate_correct_type_of_configuration_attribute("app__host", str)
        self.validate_correct_type_of_configuration_attribute(
            "app__port", (type(None), int))
        self.validate_correct_type_of_configuration_attribute(
            "app__open_browser", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__force_https", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__flask_secret_key", str)
        self.validate_correct_type_of_configuration_attribute(
            "app__generate_cache_control_headers", bool)

        if self.app__port:
            try:
                if not is_port_available(self.app__host, self.app__port):
                    raise ConfigurationError(
                        f"The port selected {self.app__port} is in use, please configure an open port."
                    )
            except OverflowError:
                raise ConfigurationError(f"Invalid port: {self.app__port}")
        else:
            try:
                default_server_port = int(
                    os.environ.get("CXG_SERVER_PORT", DEFAULT_SERVER_PORT))
            except ValueError:
                raise ConfigurationError(
                    "Invalid port from environment variable CXG_SERVER_PORT: "
                    + os.environ.get("CXG_SERVER_PORT"))
            try:
                self.app__port = find_available_port(self.app__host,
                                                     default_server_port)
            except OverflowError:
                raise ConfigurationError(
                    f"Invalid port: {default_server_port}")

        if self.app__debug:
            context["messagefn"](
                "in debug mode, setting verbose=True and open_browser=False")
            self.app__verbose = True
            self.app__open_browser = False
        else:
            warnings.formatwarning = custom_format_warning

        if not self.app__verbose:
            sys.tracebacklimit = 0
Exemple #4
0
    def handle_app(self, context):
        self.validate_correct_type_of_configuration_attribute(
            "app__verbose", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__debug", bool)
        self.validate_correct_type_of_configuration_attribute("app__host", str)
        self.validate_correct_type_of_configuration_attribute(
            "app__port", (type(None), int))
        self.validate_correct_type_of_configuration_attribute(
            "app__open_browser", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__force_https", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__flask_secret_key", str)
        self.validate_correct_type_of_configuration_attribute(
            "app__generate_cache_control_headers", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__server_timing_headers", bool)
        self.validate_correct_type_of_configuration_attribute(
            "app__csp_directives", (type(None), dict))
        self.validate_correct_type_of_configuration_attribute(
            "app__api_base_url", (type(None), str))
        self.validate_correct_type_of_configuration_attribute(
            "app__web_base_url", (type(None), str))

        if self.app__port:
            try:
                if not is_port_available(self.app__host, self.app__port):
                    raise ConfigurationError(
                        f"The port selected {self.app__port} is in use, please configure an open port."
                    )
            except OverflowError:
                raise ConfigurationError(f"Invalid port: {self.app__port}")
        else:
            try:
                default_server_port = int(
                    os.environ.get("CXG_SERVER_PORT", DEFAULT_SERVER_PORT))
            except ValueError:
                raise ConfigurationError(
                    "Invalid port from environment variable CXG_SERVER_PORT: "
                    + os.environ.get("CXG_SERVER_PORT"))
            try:
                self.app__port = find_available_port(self.app__host,
                                                     default_server_port)
            except OverflowError:
                raise ConfigurationError(
                    f"Invalid port: {default_server_port}")

        if self.app__debug:
            context["messagefn"](
                "in debug mode, setting verbose=True and open_browser=False")
            self.app__verbose = True
            self.app__open_browser = False
        else:
            warnings.formatwarning = custom_format_warning

        if not self.app__verbose:
            sys.tracebacklimit = 0

        # CSP Directives are a dict of string: list(string) or string: string
        if self.app__csp_directives is not None:
            for k, v in self.app__csp_directives.items():
                if not isinstance(k, str):
                    raise ConfigurationError(
                        "CSP directive names must be a string.")
                if isinstance(v, list):
                    for policy in v:
                        if not isinstance(policy, str):
                            raise ConfigurationError(
                                "CSP directive value must be a string or list of strings."
                            )
                elif not isinstance(v, str):
                    raise ConfigurationError(
                        "CSP directive value must be a string or list of strings."
                    )

        if self.app__web_base_url is None:
            self.app__web_base_url = self.app__api_base_url