diff --git a/client/src/globals.ts b/client/src/globals.ts index 8d6d42ced..7cc51d685 100644 --- a/client/src/globals.ts +++ b/client/src/globals.ts @@ -22,10 +22,12 @@ export const HEADER_HEIGHT_PX = 56; export const QUERY_PARAM_EXPLAIN_NEW_TAB = "explainNewTab"; /** - * Matches "/" followed by "ONE_OR_MORE_ANY_CHAR/ONE_OR_MORE_ANY_CHAR_EXCEPT_FORWARD_SLASH/" and ending with "api". Must - * exclude forward slash to prevent matches on multiple path segments (e.g. /cellxgene/d/uuid.cxg). + * This regular expression is designed to match specific API path patterns. It looks for a leading slash "/", + * followed by any number of characters until another slash "/", then any characters except for a forward slash "/", + * ending with another slash "/" and the string "api". The pattern is careful to exclude forward slashes in certain + * positions to avoid matching across multiple path segments, such as in the example path "/cellxgene/d/uuid.cxg". */ -const REGEX_PATHNAME = /(?<=\/)\w+\/[^/]+\/(?=api)/; +const REGEX_PATHNAME = /(?<=\/)\w+(\/[^/]+)*\/[^/]+\/(?=api)/; /* Config links types */ export type ConfigLink = "about-dataset" | "collections-home-page"; diff --git a/server/app/api/v3.py b/server/app/api/v3.py index 089737ff0..91b38c6b9 100644 --- a/server/app/api/v3.py +++ b/server/app/api/v3.py @@ -188,7 +188,7 @@ def get_api_dataroot_resources(bp_dataroot, url_dataroot=None): def add_resource(resource, url): """convenience function to make the outer function less verbose""" - api.add_resource(resource, url, resource_class_args=(url_dataroot,)) + api.add_resource(resource, url, resource_class_args=(url_dataroot,), strict_slashes=False) # Initialization routes add_resource(S3URIAPI, "/s3_uri") @@ -202,7 +202,7 @@ def get_api_s3uri_resources(bp_dataroot, s3uri_path): def add_resource(resource, url): """convenience function to make the outer function less verbose""" - api.add_resource(resource, url, resource_class_args=(s3uri_path,)) + api.add_resource(resource, url, resource_class_args=(s3uri_path,), strict_slashes=False) # Initialization routes add_resource(SchemaAPI, "/schema") @@ -222,12 +222,12 @@ def add_resource(resource, url): return api -def register_api_v3(app, app_config, api_url_prefix): +def register_api_v3(app, app_config, api_url_prefix, cellguide_api_url_prefix): api_version = "/api/v0.3" s3uri_api_path = "s3_uri" bp_s3uri = Blueprint( - f"api_dataset_{s3uri_api_path}_{api_version.replace('.',',')}", + f"api_{s3uri_api_path}_{api_version.replace('.',',')}", __name__, url_prefix=(f"{api_url_prefix}/{s3uri_api_path}/" + api_version).replace("//", "/"), ) @@ -236,11 +236,9 @@ def register_api_v3(app, app_config, api_url_prefix): Api(app).add_resource(VersionAPI, "/deployed_version") - # NOTE: These routes only allow the dataset to be in the directory - # of the dataroot, and not a subdirectory. We may want to change - # the route format at some point for dataroot_dict in app_config.server__multi_dataset__dataroots.values(): url_dataroot = dataroot_dict["base_url"] + bp_dataroot = Blueprint( name=f"api_dataset_{url_dataroot}_{api_version.replace('.',',')}", import_name=__name__, @@ -248,9 +246,25 @@ def register_api_v3(app, app_config, api_url_prefix): ) dataroot_resources = get_api_dataroot_resources(bp_dataroot, url_dataroot) app.register_blueprint(dataroot_resources.blueprint) + + bp_dataroot_cg = Blueprint( + name=f"api_dataset_{url_dataroot}_cellguide_cxgs_{api_version.replace('.',',')}", + import_name=__name__, + url_prefix=(f"{api_url_prefix}/{url_dataroot}/" + api_version).replace("//", "/"), + ) + + dataroot_resources_cg = get_api_dataroot_resources(bp_dataroot_cg, url_dataroot) + app.register_blueprint(dataroot_resources_cg.blueprint) + app.add_url_rule( f"/{url_dataroot}//static/", f"static_assets_{url_dataroot}", view_func=lambda dataset, filename: send_from_directory("../common/web/static", filename), methods=["GET"], ) + app.add_url_rule( + f"/{url_dataroot}//static//", + f"static_assets_{url_dataroot}_cellguide_cxgs/", + view_func=lambda dataset, filename: send_from_directory("../common/web/static", filename), + methods=["GET"], + ) diff --git a/server/app/app.py b/server/app/app.py index d91f22923..d42a1bf05 100644 --- a/server/app/app.py +++ b/server/app/app.py @@ -6,7 +6,18 @@ from http import HTTPStatus from urllib.parse import urlparse -from flask import Blueprint, Flask, Response, abort, current_app, g, make_response, redirect, render_template, request +from flask import ( + Blueprint, + Flask, + Response, + abort, + current_app, + g, + make_response, + redirect, + render_template, + request, +) from flask_restful import Api, Resource from server_timing import Timing as ServerTiming @@ -177,19 +188,27 @@ def __init__(self, app_config: AppConfig): base_resources = get_api_base_resources(bp_base) self.app.register_blueprint(base_resources.blueprint) - register_api_v3(app=self.app, app_config=app_config, api_url_prefix=api_url_prefix) + register_api_v3( + app=self.app, + app_config=app_config, + api_url_prefix=api_url_prefix, + cellguide_api_url_prefix=f"{api_url_prefix}cellguide-cxgs/", + ) - # NOTE: These routes only allow the dataset to be in the directory - # of the dataroot, and not a subdirectory. We may want to change - # the route format at some point for dataroot_dict in app_config.server__multi_dataset__dataroots.values(): url_dataroot = dataroot_dict["base_url"] self.app.add_url_rule( - f"/{url_dataroot}//", + f"/{url_dataroot}//", f"dataset_index_{url_dataroot}/", lambda dataset, url_dataroot=url_dataroot: dataset_index(url_dataroot, dataset), methods=["GET"], ) + self.app.add_url_rule( + f"/{url_dataroot}/.cxg/", + f"dataset_index_{url_dataroot}_cellguide_cxgs/", + lambda dataset, url_dataroot=url_dataroot: dataset_index(url_dataroot, f"{dataset}.cxg"), + methods=["GET"], + ) self.app.app_config = app_config