Просмотр исходного кода

feat: loader update js sdk (#12388)

* feat: Update JS SDK

* feat: Update loader code

* feat: Prepare loader for major js version bump

* feat: Add more tests

* fix: Use default version for new projects

* fix: tests

* fix: use settings var for tests

* feat: Add tests for larger versions
Daniel Griesser 6 лет назад
Родитель
Сommit
86661e5586

+ 1 - 1
package.json

@@ -18,7 +18,7 @@
     "@babel/preset-env": "^7.0.0",
     "@babel/preset-react": "^7.0.0",
     "@babel/runtime": "^7.0.0",
-    "@sentry/browser": "^4.5.2",
+    "@sentry/browser": "^4.6.4",
     "algoliasearch": "^3.32.0",
     "babel-core": "^7.0.0-bridge.0",
     "babel-loader": "^8.0.0",

+ 2 - 2
src/sentry/api/endpoints/project_keys.py

@@ -9,7 +9,7 @@ from sentry.api.bases.project import ProjectEndpoint
 from sentry.api.serializers import serialize
 from sentry.models import AuditLogEntryEvent, ProjectKey, ProjectKeyStatus
 from sentry.utils.apidocs import scenario, attach_scenarios
-from sentry.loader.browsersdkversion import get_highest_browser_sdk_version
+from sentry.loader.browsersdkversion import DEFAULT_VERSION
 
 
 @scenario('ListClientKeys')
@@ -98,7 +98,7 @@ class ProjectKeysEndpoint(ProjectEndpoint):
                 label=result.get('name'),
                 public_key=result.get('public'),
                 secret_key=result.get('secret'),
-                data={'browserSdkVersion': get_highest_browser_sdk_version()}
+                data={'browserSdkVersion': DEFAULT_VERSION}
             )
 
             self.create_audit_entry(

+ 24 - 16
src/sentry/loader/browsersdkversion.py

@@ -4,7 +4,9 @@ import os
 import re
 import logging
 import json
+import six
 
+from pkg_resources import parse_version
 from functools32 import lru_cache
 
 import sentry
@@ -13,9 +15,9 @@ from django.conf import settings
 
 logger = logging.getLogger('sentry')
 
-_version_regexp = re.compile(r'\d+')
+_version_regexp = re.compile(r'^\d+\.\d+\.\d+$')  # We really only want stable releases
 LOADER_FOLDER = os.path.abspath(os.path.join(os.path.dirname(sentry.__file__), 'loader'))
-DEFAULT_VERSION = '4.x'
+DEFAULT_VERSION = '4.x'  # DEFAULT_VERSION must exists, in case of 5.0 a new constant should be introduced
 
 
 @lru_cache(maxsize=10)
@@ -30,11 +32,10 @@ def load_registry(path):
         return None
 
 
-def get_highest_browser_sdk_version():
-    return max(get_browser_sdk_version_versions(),
-               key=lambda version: int(_version_regexp.match(version).group(0))
-               if _version_regexp.search(version) else -1
-               )
+def get_highest_browser_sdk_version(versions):
+    full_versions = filter(lambda x: _version_regexp.match(x), versions)
+    return six.binary_type(max(map(parse_version, full_versions))
+                           ) if full_versions else settings.JS_SDK_LOADER_SDK_VERSION
 
 
 def get_browser_sdk_version_versions():
@@ -50,20 +51,27 @@ def get_browser_sdk_version_choices():
 
 def load_version_from_file():
     data = load_registry('_registry')
-    return data['version']
+    if data:
+        return data.get('versions', [])
+    return []
+
+
+def get_highest_selected_browser_sdk_version(selected_version):
+    versions = load_version_from_file()
+    if selected_version == 'latest':
+        return get_highest_browser_sdk_version(versions)
+    return get_highest_browser_sdk_version(
+        filter(lambda x: x.startswith(selected_version[0]), versions))
 
 
 def get_browser_sdk_version(project_key):
     selected_version = get_selected_browser_sdk_version(project_key)
 
-    if selected_version == DEFAULT_VERSION:
-        try:
-            return load_version_from_file()
-        except BaseException:
-            logger.error('error ocurred while trying to read js sdk information from the registry')
-            return settings.JS_SDK_LOADER_SDK_VERSION
-
-    return settings.JS_SDK_LOADER_SDK_VERSION
+    try:
+        return get_highest_selected_browser_sdk_version(selected_version)
+    except BaseException:
+        logger.error('error ocurred while trying to read js sdk information from the registry')
+        return settings.JS_SDK_LOADER_SDK_VERSION
 
 
 def get_selected_browser_sdk_version(project_key):

+ 1 - 1
src/sentry/static/sentry/app/views/settings/project/projectKeys/projectKeyDetails.jsx

@@ -387,7 +387,7 @@ const KeySettings = createReactClass({
                 initialData={data}
               >
                 <Panel>
-                  <PanelHeader>{t('CDN')}</PanelHeader>
+                  <PanelHeader>{t('JavaScript Loader')}</PanelHeader>
                   <PanelBody>
                     <Field
                       help={tct(

+ 18 - 11
src/sentry/templates/sentry/js-sdk-loader.js.tmpl

@@ -95,35 +95,42 @@
 
   function sdkLoaded(callbacks, SDK) {
     try {
+      var data = queue.data;
+
+      // We have to make sure to call all callbacks first
       for (var i = 0; i < callbacks.length; i++) {
         if (typeof callbacks[i] === 'function') {
           callbacks[i]();
         }
       }
 
-      var data = queue.data;
+      var initAlreadyCalled = false;
+      var __sentry = _window['__SENTRY__'];
+      // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked
+      if (!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient()) {
+        initAlreadyCalled = true;
+      }
 
-      // We want to replay all calls to Sentry first to make sure init is called before
-      // we call all our internal error handlers
-      var firstInitCall = false;
+      // We want to replay all calls to Sentry and also make sure that `init` is called if it wasn't already
+      // We replay all calls to `Sentry.*` now
       var calledSentry = false;
       for (var i = 0; i < data.length; i++) {
         if (data[i].f) {
           calledSentry = true;
           var call = data[i];
-          if (firstInitCall === false && call.f !== 'init') {
-            // First call always has to be init, this is a conveniece for the user
-            // so call to init is optional
+          if (initAlreadyCalled === false && call.f !== 'init') {
+            // First call always has to be init, this is a conveniece for the user so call to init is optional
             SDK.init();
           }
-          firstInitCall = true;
+          initAlreadyCalled = true;
           SDK[call.f].apply(SDK, call.a);
         }
       }
-      if (calledSentry === false) {
+      if (initAlreadyCalled === false && calledSentry === false) {
         // Sentry has never been called but we need Sentry.init() so call it
         SDK.init();
       }
+
       // Because we installed the SDK, at this point we have an access to TraceKit's handler,
       // which can take care of browser differences (eg. missing exception argument in onerror)
       var tracekitErrorHandler = _window[_onerror];
@@ -199,8 +206,8 @@
   };
 
   if (!lazy) {
-    setTimeout(function() {
+    setTimeout(function () {
       injectSdk(onLoadCallbacks);
     });
   }
-})(window, document, "script", "onerror", "onunhandledrejection", "Sentry", "{{ publicKey|safe }}", "{{ jsSdkUrl|safe }}", {{ config|to_json|safe }});
+})(window, document, 'script', 'onerror', 'onunhandledrejection', 'Sentry', '{{ publicKey|safe }}', '{{ jsSdkUrl|safe }}', {{ config|to_json|safe }});

+ 4 - 4
src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl

@@ -1,4 +1,4 @@
-{% load sentry_helpers %}(function(c,t,u,n,p,l,y,z,v){function e(b){if(!w){w=!0;var d=t.getElementsByTagName(u)[0],a=t.createElement(u);a.src=z;a.crossorigin="anonymous";a.addEventListener("load",function(){try{c[n]=q;c[p]=r;var a=c[l],d=a.init;a.init=function(a){for(var b in a)Object.prototype.hasOwnProperty.call(a,b)&&(v[b]=a[b]);d(v)};B(b,a)}catch(A){console.error(A)}});d.parentNode.insertBefore(a,d)}}function B(b,d){try{for(var a=0;a<b.length;a++)if("function"===typeof b[a])b[a]();var f=m.data,g=!1,h=!1;for(a=0;a<f.length;a++)if(f[a].f){h=
-!0;var e=f[a];!1===g&&"init"!==e.f&&d.init();g=!0;d[e.f].apply(d,e.a)}!1===h&&d.init();var k=c[n],l=c[p];for(a=0;a<f.length;a++)f[a].e&&k?k.apply(c,f[a].e):f[a].p&&l&&l.apply(c,[f[a].p])}catch(C){console.error(C)}}for(var g=!0,x=!1,k=0;k<document.scripts.length;k++)if(-1<document.scripts[k].src.indexOf(y)){g="no"!==document.scripts[k].getAttribute("data-lazy");break}var w=!1,h=[],m=function(b){(b.e||b.p||b.f&&-1<b.f.indexOf("capture")||b.f&&-1<b.f.indexOf("showReportDialog"))&&g&&e(h);m.data.push(b)};
-m.data=[];c[l]={onLoad:function(b){h.push(b);g&&!x||e(h)},forceLoad:function(){x=!0;g&&setTimeout(function(){e(h)})}};"init addBreadcrumb captureMessage captureException captureEvent configureScope withScope showReportDialog".split(" ").forEach(function(b){c[l][b]=function(){m({f:b,a:arguments})}});var q=c[n];c[n]=function(b,d,a,f,e){m({e:[].slice.call(arguments)});q&&q.apply(c,arguments)};var r=c[p];c[p]=function(b){m({p:b.reason});r&&r.apply(c,arguments)};g||setTimeout(function(){e(h)})})(window,document,
-"script","onerror","onunhandledrejection","Sentry","{{ publicKey|safe }}","{{ jsSdkUrl|safe }}",{{ config|to_json|safe }});
+{% load sentry_helpers %}(function(c,u,v,n,p,q,z,A,w){function h(a){if(!x){x=!0;var k=u.getElementsByTagName(v)[0],d=u.createElement(v);d.src=A;d.crossorigin="anonymous";d.addEventListener("load",function(){try{c[n]=r;c[p]=t;var b=c[q],d=b.init;b.init=function(a){for(var b in a)Object.prototype.hasOwnProperty.call(a,b)&&(w[b]=a[b]);d(w)};B(a,b)}catch(g){console.error(g)}});k.parentNode.insertBefore(d,k)}}function B(a,k){try{for(var d=m.data,b=0;b<a.length;b++)if("function"===typeof a[b])a[b]();var e=!1,g=c.__SENTRY__;"undefined"!==
+typeof g&&g.hub&&g.hub.getClient()&&(e=!0);g=!1;for(b=0;b<d.length;b++)if(d[b].f){g=!0;var f=d[b];!1===e&&"init"!==f.f&&k.init();e=!0;k[f.f].apply(k,f.a)}!1===e&&!1===g&&k.init();var h=c[n],l=c[p];for(b=0;b<d.length;b++)d[b].e&&h?h.apply(c,d[b].e):d[b].p&&l&&l.apply(c,[d[b].p])}catch(C){console.error(C)}}for(var e=!0,y=!1,l=0;l<document.scripts.length;l++)if(-1<document.scripts[l].src.indexOf(z)){e="no"!==document.scripts[l].getAttribute("data-lazy");break}var x=!1,f=[],m=function(a){(a.e||a.p||a.f&&
+-1<a.f.indexOf("capture")||a.f&&-1<a.f.indexOf("showReportDialog"))&&e&&h(f);m.data.push(a)};m.data=[];c[q]={onLoad:function(a){f.push(a);e&&!y||h(f)},forceLoad:function(){y=!0;e&&setTimeout(function(){h(f)})}};"init addBreadcrumb captureMessage captureException captureEvent configureScope withScope showReportDialog".split(" ").forEach(function(a){c[q][a]=function(){m({f:a,a:arguments})}});var r=c[n];c[n]=function(a,e,d,b,f){m({e:[].slice.call(arguments)});r&&r.apply(c,arguments)};var t=c[p];c[p]=
+function(a){m({p:a.reason});t&&t.apply(c,arguments)};e||setTimeout(function(){h(f)})})(window,document,"script","onerror","onunhandledrejection","Sentry","{{ publicKey|safe }}","{{ jsSdkUrl|safe }}",{{ config|to_json|safe }});

+ 1 - 1
src/sentry/utils/distutils/commands/build_js_sdk_registry.py

@@ -9,7 +9,7 @@ from distutils import log
 
 import sentry
 
-JS_SDK_REGISTRY_URL = 'https://release-registry.services.sentry.io/packages/npm:@sentry/browser/latest'
+JS_SDK_REGISTRY_URL = 'https://release-registry.services.sentry.io/sdks/sentry.javascript.browser/versions'
 LOADER_FOLDER = os.path.abspath(os.path.join(os.path.dirname(sentry.__file__), 'loader'))
 
 # We cannot leverage six here, so we need to vendor

+ 36 - 3
tests/sentry/loader/test_browsersdkversion.py

@@ -1,16 +1,49 @@
 from __future__ import absolute_import
 
+import mock
+
+from django.conf import settings
 from sentry.testutils import TestCase
 from sentry.loader.browsersdkversion import (
     get_highest_browser_sdk_version,
-    get_browser_sdk_version_versions
+    get_browser_sdk_version_versions,
+    get_highest_selected_browser_sdk_version
 )
 
 
+MOCK_VERSIONS = [
+    "4.0.0-rc.1",
+    "4.6.4",
+    "4.7.4-beta1",
+    "5.0.0",
+    "5.0.1",
+    "5.0.2-beta1",
+    "5.1.1",
+    "5.10.1",
+]
+
+
 class BrowserSdkVersionTestCase(TestCase):
     def test_get_browser_sdk_version_versions(self):
         assert 'latest' in get_browser_sdk_version_versions()
         assert '4.x' in get_browser_sdk_version_versions()
 
-    def test_get_highest_browser_sdk_version(self):
-        assert get_highest_browser_sdk_version() == '4.x'
+    @mock.patch('sentry.loader.browsersdkversion.load_version_from_file',
+                return_value=MOCK_VERSIONS)
+    def test_get_highest_browser_sdk_version_from_versions(self, load_version_from_file):
+        assert get_highest_browser_sdk_version(load_version_from_file()) == '5.10.1'
+
+    @mock.patch('sentry.loader.browsersdkversion.load_version_from_file',
+                return_value=MOCK_VERSIONS)
+    def test_get_highest_selected_version(self, load_version_from_file):
+        assert get_highest_selected_browser_sdk_version('4.x') == '4.6.4'
+        assert get_highest_selected_browser_sdk_version('5.x') == '5.10.1'
+        assert get_highest_selected_browser_sdk_version('latest') == '5.10.1'
+
+    @mock.patch('sentry.loader.browsersdkversion.load_version_from_file',
+                return_value=[])
+    def test_get_highest_selected_version_no_version(self, load_version_from_file):
+        assert get_highest_selected_browser_sdk_version('4.x') == settings.JS_SDK_LOADER_SDK_VERSION
+        assert get_highest_selected_browser_sdk_version('5.x') == settings.JS_SDK_LOADER_SDK_VERSION
+        assert get_highest_selected_browser_sdk_version(
+            'latest') == settings.JS_SDK_LOADER_SDK_VERSION

+ 2 - 2
tests/sentry/web/frontend/test_js_sdk_loader.py

@@ -58,8 +58,8 @@ class JavaScriptSdkLoaderTest(TestCase):
 
     @patch('sentry.loader.browsersdkversion.load_version_from_file')
     def test_headers(self, mock_load_version_from_file):
-        mocked_version = '9.9.9'
-        mock_load_version_from_file.return_value = mocked_version
+        mocked_version = '4.9.9'
+        mock_load_version_from_file.return_value = [mocked_version]
 
         resp = self.client.get(self.path)
         assert resp.status_code == 200, resp

+ 37 - 37
yarn.lock

@@ -1200,56 +1200,56 @@
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
   integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
 
-"@sentry/browser@^4.5.2":
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-4.5.2.tgz#d13a96a0638d16af47edef25b613d96ba3f9dc21"
-  integrity sha512-Gi0wKzKokn9ZYI7MCTe3ev36npI12127QgyZNwyyG9vcR9J62cH6cltQ3BTefghJaSjgwDAfy5F/+htUzE5eCQ==
-  dependencies:
-    "@sentry/core" "4.5.2"
-    "@sentry/types" "4.5.0"
-    "@sentry/utils" "4.5.2"
+"@sentry/browser@^4.6.4":
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-4.6.4.tgz#94e376be7bb313b6faf9e40950405897dd1c1605"
+  integrity sha512-w2ITpQbs2vTKS5vtPXDgeDyr+5C4lCnTXugJrqn8u8w/XaDb3vRogfMWpQcaUENllO5xdZSItSAAHsQucY/LvA==
+  dependencies:
+    "@sentry/core" "4.6.4"
+    "@sentry/types" "4.5.3"
+    "@sentry/utils" "4.6.4"
     tslib "^1.9.3"
 
-"@sentry/core@4.5.2":
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/@sentry/core/-/core-4.5.2.tgz#b103b659857e0fabf7219e9f34c4070bcb2af0c2"
-  integrity sha512-TRUQWQugcn2ut7eII6WxU3wlUs4CSYTUqjUDne8vCMjI4oZhmGEcRUEo03OcetieH8e+oB/a1LbLMg+JI6Nsxg==
+"@sentry/core@4.6.4":
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/@sentry/core/-/core-4.6.4.tgz#7236e08115423b81b96a13c2c37f29bcc1477745"
+  integrity sha512-NGl2nkAaQ8dGqJAMS1Hb+7RyVjW4tmCbK6d7H/zKnOpBuU+qSW4XCm2NoGLLa8qb4SZUPIBRv6U0ByvEQlGtqw==
   dependencies:
-    "@sentry/hub" "4.5.2"
-    "@sentry/minimal" "4.5.2"
-    "@sentry/types" "4.5.0"
-    "@sentry/utils" "4.5.2"
+    "@sentry/hub" "4.6.4"
+    "@sentry/minimal" "4.6.4"
+    "@sentry/types" "4.5.3"
+    "@sentry/utils" "4.6.4"
     tslib "^1.9.3"
 
-"@sentry/hub@4.5.2":
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-4.5.2.tgz#b1df97dccc8644f6ef9b36e8747fe04342cb92ef"
-  integrity sha512-17mVTzYCPVdVSBOYrpr53tDTfFcSBNb92dIIEvSybZe4IWDuMRe4hhUAy+A35HesEZS68uTQq49Ntspms5B8ow==
+"@sentry/hub@4.6.4":
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-4.6.4.tgz#2bd5d67ccd43d4f5afc45005a330a11b14d46cea"
+  integrity sha512-R3ACxUZbrAMP6vyIvt1k4bE3OIyg1CzbEhzknKljPrk1abVmJVP7W/X1vBysdRtI3m/9RjOSO7Lxx3XXqoHoQg==
   dependencies:
-    "@sentry/types" "4.5.0"
-    "@sentry/utils" "4.5.2"
+    "@sentry/types" "4.5.3"
+    "@sentry/utils" "4.6.4"
     tslib "^1.9.3"
 
-"@sentry/minimal@4.5.2":
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-4.5.2.tgz#c090427f6ac276b4dc449e8be460ca5b35ce4fb3"
-  integrity sha512-HhOw055ZhjUJkJIq3/tBBE3H8tC5J64UKggrzJ1i7GinCecHBbH/RZ55zMqb3ckHPa6aFshq0hJ1+lHxeYz1Rg==
+"@sentry/minimal@4.6.4":
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-4.6.4.tgz#dc4bb47df90dad6025d832852ac11fe29ed50147"
+  integrity sha512-jZa9mfzDzJI98tg6uxFG3gdVLyz0nOHpLP9H8Kn/BelZ7WEG/ogB8PDi1hI9JvCTXAr8kV81mEecldADa9L9Yg==
   dependencies:
-    "@sentry/hub" "4.5.2"
-    "@sentry/types" "4.5.0"
+    "@sentry/hub" "4.6.4"
+    "@sentry/types" "4.5.3"
     tslib "^1.9.3"
 
-"@sentry/types@4.5.0":
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/@sentry/types/-/types-4.5.0.tgz#59e2a27d48b01b44e8959aa5c8a30514fe1086a9"
-  integrity sha512-IstPjFoebQGrQWdM732D/+S0BTovmDgezyplk4kLEb87+/B+YK0hlhziUa7B2byTFJGgQQKXy7h2sKZBrA3GUA==
+"@sentry/types@4.5.3":
+  version "4.5.3"
+  resolved "https://registry.yarnpkg.com/@sentry/types/-/types-4.5.3.tgz#3350dce2b7f9b936a8c327891c12e3aef7bd8852"
+  integrity sha512-7ll1PAFNjrBNX9rzy3P2qAQrpQwHaDO3uKj735qsnGw34OtAS8Xr8WYrjI14f9fMPa/XIeWvMPb4GMic28V/ag==
 
-"@sentry/utils@4.5.2":
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-4.5.2.tgz#0de5bef1c71e0dd800d378c010e5bfad91add91a"
-  integrity sha512-ED4CkT/zmc7LZLW5OuWRkgqcSqwZELh2S0ALJTv/4MhFZYuPWrs4DF/jcJcRkwivSoCUsiWDiESlU0JzYflbcg==
+"@sentry/utils@4.6.4":
+  version "4.6.4"
+  resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-4.6.4.tgz#ca254c142b519b4f20d63c2f9edf1a89966be36f"
+  integrity sha512-Tc5R46z7ve9Z+uU34ceDoEUR7skfQgXVIZqjbrTQphgm6EcMSNdRfkK3SJYZL5MNKiKhb7Tt/O3aPBy5bTZy6w==
   dependencies:
-    "@sentry/types" "4.5.0"
+    "@sentry/types" "4.5.3"
     tslib "^1.9.3"
 
 "@storybook/addon-a11y@^4.1.3":