diff --git a/src/sentry/conf/server.py b/src/sentry/conf/server.py index 7717fda44687e9..3dc7c562ebe384 100644 --- a/src/sentry/conf/server.py +++ b/src/sentry/conf/server.py @@ -2530,6 +2530,7 @@ def build_cdc_postgres_init_db_volume(settings): # - 'https://browser.sentry-cdn.com/6.19.7/bundle.min.js' will stay the same. JS_SDK_LOADER_DEFAULT_SDK_URL = "" + # block domains which are generally used by spammers -- keep this configurable # in case a self-hosted install wants to allow it INVALID_EMAIL_ADDRESS_PATTERN = re.compile(r"\@qq\.com$", re.I) diff --git a/src/sentry/web/frontend/js_sdk_dynamic_loader.py b/src/sentry/web/frontend/js_sdk_dynamic_loader.py new file mode 100644 index 00000000000000..20a48c81f0a006 --- /dev/null +++ b/src/sentry/web/frontend/js_sdk_dynamic_loader.py @@ -0,0 +1,69 @@ +from typing import Optional, Tuple, TypedDict + +from rest_framework.request import Request +from rest_framework.response import Response + +from sentry.loader.browsersdkversion import get_browser_sdk_version +from sentry.models import Project, ProjectKey +from sentry.web.frontend.base import BaseView + + +class SdkConfig(TypedDict): + dsn: str + + +class LoaderContext(TypedDict): + config: SdkConfig + jsSdkUrl: Optional[str] + publicKey: Optional[str] + + +class JavaScriptSdkDynamicLoader(BaseView): + auth_required = False + + # Do not let an organization load trigger session, breaking Vary header. + # TODO: This view should probably not be a subclass of BaseView if it doesn't actually use the + # large amount of organization related support utilities, but that ends up being a large refactor. + def determine_active_organization(self, request: Request, organization_slug=None) -> None: + pass + + def _get_bundle_kind_modifier(self) -> str: + """Returns a string that is used to modify the bundle name""" + bundle_kind_modifier = "" + + # if tracing + bundle_kind_modifier += ".tracing" + + # if replay + bundle_kind_modifier += ".replay" + + # if es5 + bundle_kind_modifier += ".es5" + + # if debug + bundle_kind_modifier += ".debug" + + return bundle_kind_modifier + + def _get_context(self, key: str) -> Tuple[LoaderContext, Optional[str], Optional[str]]: + """Sets context information needed to render the loader""" + if not key: + return ({}, None, None) + + sdk_version = get_browser_sdk_version(key) + + # TODO(abhi): Right now this loader only supports returning es6 JS bundles. + # We may want to re-evaluate this. + + return ({}, sdk_version, None) + + def get(self, request: Request, public_key: str, minified: str) -> Response: + """Returns a JS file that dynamically loads the SDK based on project settings""" + key = None + + try: + key = ProjectKey.objects.get_from_cache(public_key=public_key) + except ProjectKey.DoesNotExist: + pass + else: + key.project = Project.objects.get_from_cache(id=key.project_id) diff --git a/src/sentry/web/urls.py b/src/sentry/web/urls.py index 2983f23c9df8a4..49a1b5402409b1 100644 --- a/src/sentry/web/urls.py +++ b/src/sentry/web/urls.py @@ -23,6 +23,7 @@ from sentry.web.frontend.group_tag_export import GroupTagExportView from sentry.web.frontend.home import HomeView from sentry.web.frontend.idp_email_verification import AccountConfirmationView +from sentry.web.frontend.js_sdk_dynamic_loader import JavaScriptSdkDynamicLoader from sentry.web.frontend.js_sdk_loader import JavaScriptSdkLoader from sentry.web.frontend.mailgun_inbound_webhook import MailgunInboundWebhookView from sentry.web.frontend.newest_performance_issue import NewestPerformanceIssueView @@ -116,6 +117,12 @@ JavaScriptSdkLoader.as_view(), name="sentry-js-sdk-loader", ), + # Javascript SDK Loader + url( + r"^js-sdk-loader/(?P[^/\.]+)(?:(?P\.min))?\.js$", + JavaScriptSdkDynamicLoader.as_view(), + name="sentry-js-sdk-loader", + ), # Versioned API url(r"^api/0/", include("sentry.api.urls")), # Legacy unversioned endpoints diff --git a/tests/sentry/web/frontend/test_js_sdk_dynamic_loader.py b/tests/sentry/web/frontend/test_js_sdk_dynamic_loader.py new file mode 100644 index 00000000000000..934a51b0d8029d --- /dev/null +++ b/tests/sentry/web/frontend/test_js_sdk_dynamic_loader.py @@ -0,0 +1,31 @@ +import pytest +from django.conf import settings + +from sentry.testutils import TestCase + + +class JavaScriptSdkDynamicLoaderTest(TestCase): + @pytest.fixture(autouse=True) + def set_settings(self): + settings.JS_SDK_LOADER_SDK_VERSION = "7.36.0" + settings.JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle%s.min.js" + + def test_noop_no_pub_key(self): + # When given no public key, respond with the noop template + pass + + def test_noop(self): + # When given a public key, but no default SDK URL, respond with the noop template + pass + + def test_no_replace(self): + # When given a public key, and a default SDK URL, but the SDK version is not set, respond with the noop template + pass + + def test_renders_js_loader(self): + # When given a public key, and a default SDK URL, and the SDK version is set, respond with the js loader template + pass + + def test_minified(self): + # When given a public key, and a default SDK URL, and the SDK version is set, respond with the minified js loader template + pass