Browse Source

[ui_analytics] prevent incorrect log calls to the backend and GA

Björn Alm 3 years ago
parent
commit
9343d97e71

+ 59 - 0
desktop/core/src/desktop/js/utils/hueAnalytics.test.ts

@@ -91,6 +91,65 @@ describe('hueAnalytics', () => {
     });
   });
 
+  it('should not log to backend or GA with incorrect parameters', () => {
+    const gtagSpy = jest.fn();
+    getLastKnownConfigMock.mockImplementation(() => {
+      return { hue_config: { collect_usage: true } };
+    });
+
+    windowSpy.mockImplementation(() => ({
+      DEV: false,
+      gtag: gtagSpy
+    }));
+
+    windowSpy.mockImplementation(() => ({}));
+
+    hueAnalytics.convert(null, 'action1');
+    hueAnalytics.convert(undefined, 'action1');
+    hueAnalytics.convert([], 'action1');
+    hueAnalytics.convert({}, 'action1');
+    hueAnalytics.convert(NaN, 'action1');
+    hueAnalytics.convert(1, 'action1');
+    hueAnalytics.convert(true, 'action1');
+    hueAnalytics.convert(new Date(), 'action1');
+    hueAnalytics.convert(BigInt(1), 'action1');
+    hueAnalytics.convert(Symbol(), 'action1');
+    hueAnalytics.convert('area1', null);
+    hueAnalytics.convert('area1', undefined);
+    hueAnalytics.convert('area1', []);
+    hueAnalytics.convert('area1', {});
+    hueAnalytics.convert('area1', NaN);
+    hueAnalytics.convert('area1', 1);
+    hueAnalytics.convert('area1', true);
+    hueAnalytics.convert('area1', new Date());
+    hueAnalytics.convert('area1', BigInt(1));
+    hueAnalytics.convert('area1', Symbol());
+
+    hueAnalytics.log(null, 'action1');
+    hueAnalytics.log(undefined, 'action1');
+    hueAnalytics.log([], 'action1');
+    hueAnalytics.log({}, 'action1');
+    hueAnalytics.log(NaN, 'action1');
+    hueAnalytics.log(1, 'action1');
+    hueAnalytics.log(true, 'action1');
+    hueAnalytics.log(new Date(), 'action1');
+    hueAnalytics.log(BigInt(1), 'action1');
+    hueAnalytics.log(Symbol(), 'action1');
+    hueAnalytics.log('area1', null);
+    hueAnalytics.log('area1', undefined);
+    hueAnalytics.log('area1', []);
+    hueAnalytics.log('area1', {});
+    hueAnalytics.log('area1', NaN);
+    hueAnalytics.log('area1', 1);
+    hueAnalytics.log('area1', true);
+    hueAnalytics.log('area1', new Date());
+    hueAnalytics.log('area1', BigInt(1));
+    hueAnalytics.log('area1', Symbol());
+
+    expect(gtagSpy).not.toHaveBeenCalled();
+    expect(postSpy).not.toHaveBeenCalled();
+  });
+
   it('should use global listener to log clicks on element with attribute "data-hue-analytics"', () => {
     const gtagSpy = jest.fn();
     getLastKnownConfigMock.mockImplementation(() => {

+ 24 - 0
desktop/core/src/desktop/js/utils/hueAnalytics.ts

@@ -40,6 +40,22 @@ const formatGaData = (
   }
 });
 
+// Check and warning for when the analytics log/convert are called incorrectly
+// by legacy non typescript code. We want this to fail silenctly in hueDebugAnalytics mode.
+const validateParameterTypes = (area: string, action: string): boolean => {
+  const typedWindow = <hueWindow>window;
+  if (typedWindow.DEV && typedWindow.hueDebugAnalytics) {
+    if (typeof area !== 'string') {
+      console.error(`hueAnalytics parameter "area" must be a string`);
+    }
+    if (typeof action !== 'string') {
+      console.error(`hueAnalytics parameter "action" must be a string`);
+    }
+  }
+
+  return typeof area === 'string' && typeof action === 'string';
+};
+
 export const hueAnalytics = {
   log(area: string, action: string, isPrioritised?: boolean): void {
     const config = getLastKnownConfig();
@@ -49,6 +65,10 @@ export const hueAnalytics = {
     }
 
     if (config?.hue_config?.collect_usage) {
+      if (!validateParameterTypes(area, action)) {
+        return;
+      }
+
       const { type, name, params } = formatGaData(area, action);
 
       // Quick debug mode to check that the analytics logging is working when developing new features
@@ -64,6 +84,10 @@ export const hueAnalytics = {
     }
   },
   convert(area: string, action: string): void {
+    if (!validateParameterTypes(area, action)) {
+      return;
+    }
+
     post('/desktop/log_analytics', {
       area,
       action

+ 5 - 1
desktop/core/src/desktop/views.py

@@ -220,7 +220,11 @@ def log_js_error(request):
 
 def log_analytics(request):
   ai = AccessInfo(request)
-  ai.log(level=logging.INFO, msg='UI INTERACTION: ' + request.POST.get('area') + ' > ' + request.POST.get('action'))
+  area = request.POST.get('area')
+  action = request.POST.get('action')
+
+  if area is not None and action is not None:
+    ai.log(level=logging.INFO, msg='UI INTERACTION: ' + area + ' > ' + action)
 
   return JsonResponse({'status': 0})