mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-07 06:58:11 +08:00
revert removal of cache control headers (#2118)
* revert removal of cache control headers * always generate cache header for health route
This commit is contained in:
@@ -21,6 +21,42 @@ from backend.common.utils.utils import Float32JSONEncoder
|
|||||||
|
|
||||||
webbp = Blueprint("webapp", "backend.server.common.web", template_folder="templates")
|
webbp = Blueprint("webapp", "backend.server.common.web", template_folder="templates")
|
||||||
|
|
||||||
|
ONE_WEEK = 7 * 24 * 60 * 60
|
||||||
|
|
||||||
|
|
||||||
|
def _cache_control(always, **cache_kwargs):
|
||||||
|
"""
|
||||||
|
Used to easily manage cache control headers on responses.
|
||||||
|
See Werkzeug for attributes that can be set, eg, no_cache, private, max_age, etc.
|
||||||
|
https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.ResponseCacheControl
|
||||||
|
"""
|
||||||
|
|
||||||
|
def inner_cache_control(f):
|
||||||
|
@wraps(f)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
response = make_response(f(*args, **kwargs))
|
||||||
|
if not always and not current_app.app_config.server_config.app__generate_cache_control_headers:
|
||||||
|
return response
|
||||||
|
if response.status_code >= 400:
|
||||||
|
return response
|
||||||
|
for k, v in cache_kwargs.items():
|
||||||
|
setattr(response.cache_control, k, v)
|
||||||
|
return response
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return inner_cache_control
|
||||||
|
|
||||||
|
|
||||||
|
def cache_control(**cache_kwargs):
|
||||||
|
""" config driven """
|
||||||
|
return _cache_control(False, **cache_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def cache_control_always(**cache_kwargs):
|
||||||
|
""" always generate headers, regardless of the config """
|
||||||
|
return _cache_control(True, **cache_kwargs)
|
||||||
|
|
||||||
|
|
||||||
@webbp.route("/", methods=["GET"])
|
@webbp.route("/", methods=["GET"])
|
||||||
def dataset_index():
|
def dataset_index():
|
||||||
@@ -71,6 +107,7 @@ def rest_get_data_adaptor(func):
|
|||||||
|
|
||||||
|
|
||||||
class HealthAPI(Resource):
|
class HealthAPI(Resource):
|
||||||
|
@cache_control_always(no_store=True)
|
||||||
def get(self):
|
def get(self):
|
||||||
config = current_app.app_config
|
config = current_app.app_config
|
||||||
return health_check(config)
|
return health_check(config)
|
||||||
@@ -78,78 +115,92 @@ class HealthAPI(Resource):
|
|||||||
|
|
||||||
class SchemaAPI(Resource):
|
class SchemaAPI(Resource):
|
||||||
# TODO @mdunitz separate dataset schema and user schema
|
# TODO @mdunitz separate dataset schema and user schema
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.schema_get(data_adaptor)
|
return common_rest.schema_get(data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class ConfigAPI(Resource):
|
class ConfigAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.config_get(current_app.app_config, data_adaptor)
|
return common_rest.config_get(current_app.app_config, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class UserInfoAPI(Resource):
|
class UserInfoAPI(Resource):
|
||||||
|
@cache_control_always(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.userinfo_get(current_app.app_config, data_adaptor)
|
return common_rest.userinfo_get(current_app.app_config, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class AnnotationsObsAPI(Resource):
|
class AnnotationsObsAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.annotations_obs_get(request, data_adaptor)
|
return common_rest.annotations_obs_get(request, data_adaptor)
|
||||||
|
|
||||||
@requires_authentication
|
@requires_authentication
|
||||||
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def put(self, data_adaptor):
|
def put(self, data_adaptor):
|
||||||
return common_rest.annotations_obs_put(request, data_adaptor)
|
return common_rest.annotations_obs_put(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class AnnotationsVarAPI(Resource):
|
class AnnotationsVarAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.annotations_var_get(request, data_adaptor)
|
return common_rest.annotations_var_get(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class DataVarAPI(Resource):
|
class DataVarAPI(Resource):
|
||||||
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def put(self, data_adaptor):
|
def put(self, data_adaptor):
|
||||||
return common_rest.data_var_put(request, data_adaptor)
|
return common_rest.data_var_put(request, data_adaptor)
|
||||||
|
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.data_var_get(request, data_adaptor)
|
return common_rest.data_var_get(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class ColorsAPI(Resource):
|
class ColorsAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.colors_get(data_adaptor)
|
return common_rest.colors_get(data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class DiffExpObsAPI(Resource):
|
class DiffExpObsAPI(Resource):
|
||||||
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def post(self, data_adaptor):
|
def post(self, data_adaptor):
|
||||||
return common_rest.diffexp_obs_post(request, data_adaptor)
|
return common_rest.diffexp_obs_post(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class LayoutObsAPI(Resource):
|
class LayoutObsAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.layout_obs_get(request, data_adaptor)
|
return common_rest.layout_obs_get(request, data_adaptor)
|
||||||
|
|
||||||
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def put(self, data_adaptor):
|
def put(self, data_adaptor):
|
||||||
return common_rest.layout_obs_put(request, data_adaptor)
|
return common_rest.layout_obs_put(request, data_adaptor)
|
||||||
|
|
||||||
|
|
||||||
class GenesetsAPI(Resource):
|
class GenesetsAPI(Resource):
|
||||||
|
@cache_control(public=True, max_age=ONE_WEEK)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.genesets_get(request, data_adaptor)
|
return common_rest.genesets_get(request, data_adaptor)
|
||||||
|
|
||||||
@requires_authentication
|
@requires_authentication
|
||||||
|
@cache_control(no_store=True)
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
def put(self, data_adaptor):
|
def put(self, data_adaptor):
|
||||||
return common_rest.genesets_put(request, data_adaptor)
|
return common_rest.genesets_put(request, data_adaptor)
|
||||||
@@ -157,6 +208,7 @@ class GenesetsAPI(Resource):
|
|||||||
|
|
||||||
class GenesetSummaryAPI(Resource):
|
class GenesetSummaryAPI(Resource):
|
||||||
@rest_get_data_adaptor
|
@rest_get_data_adaptor
|
||||||
|
@cache_control(no_store=True)
|
||||||
def get(self, data_adaptor):
|
def get(self, data_adaptor):
|
||||||
return common_rest.geneset_summary_get(request, data_adaptor)
|
return common_rest.geneset_summary_get(request, data_adaptor)
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class ServerConfig(BaseConfig):
|
|||||||
self.app__open_browser = default_config["app"]["open_browser"]
|
self.app__open_browser = default_config["app"]["open_browser"]
|
||||||
self.app__force_https = default_config["app"]["force_https"]
|
self.app__force_https = default_config["app"]["force_https"]
|
||||||
self.app__flask_secret_key = default_config["app"]["flask_secret_key"]
|
self.app__flask_secret_key = default_config["app"]["flask_secret_key"]
|
||||||
|
self.app__generate_cache_control_headers = default_config["app"]["generate_cache_control_headers"]
|
||||||
|
|
||||||
self.authentication__type = default_config["authentication"]["type"]
|
self.authentication__type = default_config["authentication"]["type"]
|
||||||
self.authentication__insecure_test_environment = default_config["authentication"][
|
self.authentication__insecure_test_environment = default_config["authentication"][
|
||||||
@@ -73,6 +74,7 @@ class ServerConfig(BaseConfig):
|
|||||||
self.validate_correct_type_of_configuration_attribute("app__open_browser", bool)
|
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__force_https", bool)
|
||||||
self.validate_correct_type_of_configuration_attribute("app__flask_secret_key", str)
|
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:
|
if self.app__port:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ server:
|
|||||||
open_browser: false
|
open_browser: false
|
||||||
force_https: false
|
force_https: false
|
||||||
flask_secret_key: null
|
flask_secret_key: null
|
||||||
|
generate_cache_control_headers: false
|
||||||
|
|
||||||
authentication:
|
authentication:
|
||||||
# The authentication types may be "none" or "session"
|
# The authentication types may be "none" or "session"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ f"""server:
|
|||||||
open_browser: {open_browser}
|
open_browser: {open_browser}
|
||||||
force_https: {force_https}
|
force_https: {force_https}
|
||||||
flask_secret_key: {flask_secret_key}
|
flask_secret_key: {flask_secret_key}
|
||||||
|
generate_cache_control_headers: {generate_cache_control_headers}
|
||||||
authentication:
|
authentication:
|
||||||
type: {auth_type}
|
type: {auth_type}
|
||||||
insecure_test_environment: {insecure_test_environment}
|
insecure_test_environment: {insecure_test_environment}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class ConfigTests(unittest.TestCase):
|
|||||||
open_browser="false",
|
open_browser="false",
|
||||||
force_https="false",
|
force_https="false",
|
||||||
flask_secret_key="secret",
|
flask_secret_key="secret",
|
||||||
|
generate_cache_control_headers="false",
|
||||||
auth_type="session",
|
auth_type="session",
|
||||||
insecure_test_environment="false",
|
insecure_test_environment="false",
|
||||||
index="false",
|
index="false",
|
||||||
@@ -67,6 +68,7 @@ class ConfigTests(unittest.TestCase):
|
|||||||
open_browser="false",
|
open_browser="false",
|
||||||
force_https="false",
|
force_https="false",
|
||||||
flask_secret_key="secret",
|
flask_secret_key="secret",
|
||||||
|
generate_cache_control_headers="false",
|
||||||
auth_type="session",
|
auth_type="session",
|
||||||
index="false",
|
index="false",
|
||||||
allowed_matrix_types=[],
|
allowed_matrix_types=[],
|
||||||
@@ -116,6 +118,7 @@ class ConfigTests(unittest.TestCase):
|
|||||||
open_browser=open_browser,
|
open_browser=open_browser,
|
||||||
force_https=force_https,
|
force_https=force_https,
|
||||||
flask_secret_key=flask_secret_key,
|
flask_secret_key=flask_secret_key,
|
||||||
|
generate_cache_control_headers=generate_cache_control_headers,
|
||||||
auth_type=auth_type,
|
auth_type=auth_type,
|
||||||
index=index,
|
index=index,
|
||||||
allowed_matrix_types=allowed_matrix_types,
|
allowed_matrix_types=allowed_matrix_types,
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class TestServerConfig(ConfigTests):
|
|||||||
def test_complete_config_checks_all_attr(self, mock_check_attrs):
|
def test_complete_config_checks_all_attr(self, mock_check_attrs):
|
||||||
mock_check_attrs.side_effect = BaseConfig.validate_correct_type_of_configuration_attribute()
|
mock_check_attrs.side_effect = BaseConfig.validate_correct_type_of_configuration_attribute()
|
||||||
self.server_config.complete_config(self.context)
|
self.server_config.complete_config(self.context)
|
||||||
self.assertEqual(mock_check_attrs.call_count, 20)
|
self.assertEqual(mock_check_attrs.call_count, 21)
|
||||||
|
|
||||||
def test_handle_app__throws_error_if_port_doesnt_exist(self):
|
def test_handle_app__throws_error_if_port_doesnt_exist(self):
|
||||||
config = self.get_config(port=99999999)
|
config = self.get_config(port=99999999)
|
||||||
|
|||||||
Reference in New Issue
Block a user