Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/framework/ts_mon_js.py b/framework/ts_mon_js.py
new file mode 100644
index 0000000..61be1a8
--- /dev/null
+++ b/framework/ts_mon_js.py
@@ -0,0 +1,110 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""ts_mon JavaScript proxy handler."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+from framework import authdata
+from framework import sql
+from framework import xsrf
+
+from gae_ts_mon.handlers import TSMonJSHandler
+
+from google.appengine.api import users
+
+from infra_libs import ts_mon
+
+
+STANDARD_FIELDS = [
+  ts_mon.StringField('client_id'),
+  ts_mon.StringField('host_name'),
+  ts_mon.BooleanField('document_visible'),
+]
+
+
+# User action metrics.
+ISSUE_CREATE_LATENCY_METRIC = ts_mon.CumulativeDistributionMetric(
+  'monorail/frontend/issue_create_latency', (
+    'Latency between Issue Entry form submission and page load of '
+    'the subsequent issue page.'
+  ), field_spec=STANDARD_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+ISSUE_UPDATE_LATENCY_METRIC = ts_mon.CumulativeDistributionMetric(
+  'monorail/frontend/issue_update_latency', (
+    'Latency between Issue Update form submission and page load of '
+    'the subsequent issue page.'
+  ), field_spec=STANDARD_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+AUTOCOMPLETE_POPULATE_LATENCY_METRIC = ts_mon.CumulativeDistributionMetric(
+  'monorail/frontend/autocomplete_populate_latency', (
+    'Latency between page load and autocomplete options loading.'
+  ), field_spec=STANDARD_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+CHARTS_SWITCH_DATE_RANGE_METRIC = ts_mon.CounterMetric(
+  'monorail/frontend/charts/switch_date_range', (
+    'Number of times user clicks frequency button.'
+  ), field_spec=STANDARD_FIELDS + [ts_mon.IntegerField('date_range')])
+
+# Page load metrics.
+ISSUE_COMMENTS_LOAD_EXTRA_FIELDS = [
+  ts_mon.StringField('template_name'),
+  ts_mon.BooleanField('full_app_load'),
+]
+ISSUE_COMMENTS_LOAD_LATENCY_METRIC = ts_mon.CumulativeDistributionMetric(
+  'monorail/frontend/issue_comments_load_latency', (
+    'Time from navigation or click to issue comments loaded.'
+  ), field_spec=STANDARD_FIELDS + ISSUE_COMMENTS_LOAD_EXTRA_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+DOM_CONTENT_LOADED_EXTRA_FIELDS = [
+  ts_mon.StringField('template_name')]
+DOM_CONTENT_LOADED_METRIC = ts_mon.CumulativeDistributionMetric(
+  'frontend/dom_content_loaded', (
+    'domContentLoaded performance timing.'
+  ), field_spec=STANDARD_FIELDS + DOM_CONTENT_LOADED_EXTRA_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+
+
+ISSUE_LIST_LOAD_EXTRA_FIELDS = [
+  ts_mon.StringField('template_name'),
+  ts_mon.BooleanField('full_app_load'),
+]
+ISSUE_LIST_LOAD_LATENCY_METRIC = ts_mon.CumulativeDistributionMetric(
+  'monorail/frontend/issue_list_load_latency', (
+    'Time from navigation or click to search issues list loaded.'
+  ), field_spec=STANDARD_FIELDS + ISSUE_LIST_LOAD_EXTRA_FIELDS,
+  units=ts_mon.MetricsDataUnits.MILLISECONDS)
+
+
+class MonorailTSMonJSHandler(TSMonJSHandler):
+
+  def __init__(self, request=None, response=None):
+    super(MonorailTSMonJSHandler, self).__init__(request, response)
+    self.register_metrics([
+        ISSUE_CREATE_LATENCY_METRIC,
+        ISSUE_UPDATE_LATENCY_METRIC,
+        AUTOCOMPLETE_POPULATE_LATENCY_METRIC,
+        CHARTS_SWITCH_DATE_RANGE_METRIC,
+        ISSUE_COMMENTS_LOAD_LATENCY_METRIC,
+        DOM_CONTENT_LOADED_METRIC,
+        ISSUE_LIST_LOAD_LATENCY_METRIC])
+
+  def xsrf_is_valid(self, body):
+    """This method expects the body dictionary to include two fields:
+    `token` and `user_id`.
+    """
+    cnxn = sql.MonorailConnection()
+    token = body.get('token')
+    user = users.get_current_user()
+    email = user.email() if user else None
+
+    services = self.app.config.get('services')
+    auth = authdata.AuthData.FromEmail(cnxn, email, services, autocreate=False)
+    try:
+      xsrf.ValidateToken(token, auth.user_id, xsrf.XHR_SERVLET_PATH)
+      return True
+    except xsrf.TokenIncorrect:
+      return False