From 90ed2b621fe7a331151ce826e62e828e4cbcdbee Mon Sep 17 00:00:00 2001 From: Nick Muerdter Date: Sat, 18 Feb 2017 12:10:48 -0700 Subject: [PATCH] Fix timezone handling for analytics dates, and make date ranges relative - This fixes the default dates in the admin UI so they correspond with the analytics timezone. This prevents issues with loading the app and the default date ranges possibly not reflecting the current time according to the analytics timezone. For example, if the analytics timezone was New York (-5), but you loaded the app in Denver (-7) at 23:00, the end date was previously the current date in Denver, not the following day in New York. - Remove defunct client-side "tz" parameters. This got removed before the v0.12 release (7c87634). - Make the date range options like "Last 30 Days" relative so that shared or bookmarked URLs can always reflect the last 30 days, rather than when the page was first loaded. This had been on our radar for a while, but with the changes to make the default date ranges time-zone aware, this became a bit more relevant (to deal with Ember not really supporting dynamic defaults for URLs). See https://github.com/18F/api.data.gov/issues/73 - Add more tests around all the date handling with regards to timezones, the current date, and URL handling. --- src/api-umbrella/admin-ui/.eslintrc.js | 2 - .../stats/drilldown/results-table.js | 6 +- .../stats/logs/results-facet-table.js | 2 +- .../components/stats/logs/results-table.js | 10 +- .../app/components/stats/map/results-map.js | 4 +- .../app/components/stats/map/results-table.js | 6 +- .../app/components/stats/query-form.js | 117 ++++++---- .../components/stats/users/results-table.js | 10 +- .../admin-ui/app/controllers/stats/base.js | 6 +- .../app/controllers/stats/drilldown.js | 1 - .../admin-ui/app/controllers/stats/logs.js | 2 +- .../admin-ui/app/controllers/stats/map.js | 2 - .../admin-ui/app/controllers/stats/users.js | 1 - .../admin-ui/app/helpers/format-date.js | 1 + .../admin-ui/app/models/api/rate-limit.js | 1 + .../admin-ui/app/routes/stats/base.js | 84 +++++++- .../admin-ui/app/routes/stats/drilldown.js | 5 +- .../admin-ui/app/routes/stats/logs.js | 5 +- .../admin-ui/app/routes/stats/map.js | 5 +- .../admin-ui/app/routes/stats/users.js | 6 +- src/api-umbrella/admin-ui/app/styles/app.scss | 2 +- .../stats/logs/results-highlights.hbs | 4 +- .../app/templates/stats/drilldown.hbs | 4 +- .../admin-ui/app/templates/stats/logs.hbs | 6 +- .../admin-ui/app/templates/stats/map.hbs | 6 +- .../admin-ui/app/templates/stats/users.hbs | 4 +- .../admin-ui/app/utils/data-tables-helpers.js | 2 + src/api-umbrella/admin-ui/bower.json | 3 - src/api-umbrella/admin-ui/ember-cli-build.js | 4 - src/api-umbrella/admin-ui/package.json | 4 +- src/api-umbrella/admin-ui/yarn.lock | 42 ++-- .../controllers/admin/sessions_controller.rb | 1 + test/admin_ui/test_legacy_redirects.rb | 55 +---- test/admin_ui/test_stats_drilldown.rb | 8 +- test/admin_ui/test_stats_logs.rb | 27 +-- test/admin_ui/test_stats_map.rb | 8 +- test/admin_ui/test_stats_users.rb | 10 +- test/apis/admin/stats/test_logs.rb | 6 - test/apis/admin/stats/test_map.rb | 4 - test/apis/admin/stats/test_search.rb | 5 - test/apis/admin/test_auth.rb | 2 + test/apis/v1/analytics/test_drilldown.rb | 10 - .../api_umbrella_test_helpers/admin_auth.rb | 14 ++ .../date_range_picker.rb | 161 ++++++++++++++ test/support/capybara.rb | 1 + test/support/capybara/timekeeper.js | 201 ++++++++++++++++++ 46 files changed, 652 insertions(+), 218 deletions(-) create mode 100644 test/support/api_umbrella_test_helpers/date_range_picker.rb create mode 100644 test/support/capybara/timekeeper.js diff --git a/src/api-umbrella/admin-ui/.eslintrc.js b/src/api-umbrella/admin-ui/.eslintrc.js index 94c9bed08..efe3f046a 100644 --- a/src/api-umbrella/admin-ui/.eslintrc.js +++ b/src/api-umbrella/admin-ui/.eslintrc.js @@ -32,8 +32,6 @@ module.exports = { 'ace': true, 'bootbox': true, 'inflection': true, - 'jstz': true, 'marked': true, - 'moment': true, }, }; diff --git a/src/api-umbrella/admin-ui/app/components/stats/drilldown/results-table.js b/src/api-umbrella/admin-ui/app/components/stats/drilldown/results-table.js index 6f89aa1a9..18fc3534f 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/drilldown/results-table.js +++ b/src/api-umbrella/admin-ui/app/components/stats/drilldown/results-table.js @@ -19,7 +19,7 @@ export default Ember.Component.extend({ if(data.terminal) { return '' + _.escape(name); } else { - let params = _.clone(this.get('queryParamValues')); + let params = _.clone(this.get('presentQueryParamValues')); params.prefix = data.descendent_prefix; let link = '#/stats/drilldown?' + $.param(params); @@ -53,7 +53,7 @@ export default Ember.Component.extend({ table.draw(); }), - downloadUrl: Ember.computed('allQueryParamValues', function() { - return '/api-umbrella/v1/analytics/drilldown.csv?api_key=' + this.get('session.data.authenticated.api_key') + '&' + $.param(this.get('allQueryParamValues')); + downloadUrl: Ember.computed('backendQueryParamValues', function() { + return '/api-umbrella/v1/analytics/drilldown.csv?api_key=' + this.get('session.data.authenticated.api_key') + '&' + $.param(this.get('backendQueryParamValues')); }), }); diff --git a/src/api-umbrella/admin-ui/app/components/stats/logs/results-facet-table.js b/src/api-umbrella/admin-ui/app/components/stats/logs/results-facet-table.js index 3dac5d1a5..649601302 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/logs/results-facet-table.js +++ b/src/api-umbrella/admin-ui/app/components/stats/logs/results-facet-table.js @@ -3,7 +3,7 @@ import Ember from 'ember'; export default Ember.Component.extend({ setLinks: Ember.on('init', Ember.observer('facets', function() { _.each(this.get('facets'), function(bucket) { - let params = _.clone(this.get('queryParamValues')); + let params = _.clone(this.get('presentQueryParamValues')); params.search = _.compact([params.search, this.get('field') + ':"' + bucket.key + '"']).join(' AND '); bucket.link = '#/stats/logs?' + $.param(params); }.bind(this)); diff --git a/src/api-umbrella/admin-ui/app/components/stats/logs/results-table.js b/src/api-umbrella/admin-ui/app/components/stats/logs/results-table.js index 038c0b14c..e8d59a5e0 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/logs/results-table.js +++ b/src/api-umbrella/admin-ui/app/components/stats/logs/results-table.js @@ -12,7 +12,7 @@ export default Ember.Component.extend({ // exceed URL length limits in IE (and apparently Capybara too). type: 'POST', data: function(data) { - return _.extend({}, data, this.get('allQueryParamValues')); + return _.extend({}, data, this.get('backendQueryParamValues')); }.bind(this), }, drawCallback: _.bind(function() { @@ -70,7 +70,7 @@ export default Ember.Component.extend({ defaultContent: '-', render: function(email, type, data) { if(type === 'display' && email && email !== '-') { - let params = _.clone(this.get('queryParamValues')); + let params = _.clone(this.get('presentQueryParamValues')); params.search = _.compact([params.search, 'user_id:"' + data.user_id + '"']).join(' AND '); let link = '#/stats/logs?' + $.param(params); @@ -174,11 +174,11 @@ export default Ember.Component.extend({ }); }, - refreshData: Ember.observer('allQueryParamValues', function() { + refreshData: Ember.observer('backendQueryParamValues', function() { this.$().find('table').DataTable().draw(); }), - downloadUrl: Ember.computed('allQueryParamValues', function() { - return '/admin/stats/logs.csv?' + $.param(this.get('allQueryParamValues')); + downloadUrl: Ember.computed('backendQueryParamValues', function() { + return '/admin/stats/logs.csv?' + $.param(this.get('backendQueryParamValues')); }), }); diff --git a/src/api-umbrella/admin-ui/app/components/stats/map/results-map.js b/src/api-umbrella/admin-ui/app/components/stats/map/results-map.js index 528c7eee1..4ded99b9c 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/map/results-map.js +++ b/src/api-umbrella/admin-ui/app/components/stats/map/results-map.js @@ -20,7 +20,7 @@ export default Ember.Component.extend({ }, handleRegionClick(event) { - let queryParams = _.clone(this.get('queryParamValues')); + let queryParams = _.clone(this.get('presentQueryParamValues')); queryParams.region = event.name; this.get('routing').transitionTo('stats.map', undefined, queryParams); }, @@ -30,7 +30,7 @@ export default Ember.Component.extend({ let currentRegion = this.get('allQueryParamValues.region').split('-'); let currentCountry = currentRegion[0]; currentRegion = currentRegion[1]; - let queryParams = _.clone(this.get('queryParamValues')); + let queryParams = _.clone(this.get('presentQueryParamValues')); queryParams.query = JSON.stringify({ condition: 'AND', rules: [ diff --git a/src/api-umbrella/admin-ui/app/components/stats/map/results-table.js b/src/api-umbrella/admin-ui/app/components/stats/map/results-table.js index 2e418fa05..3419c8c10 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/map/results-table.js +++ b/src/api-umbrella/admin-ui/app/components/stats/map/results-table.js @@ -15,7 +15,7 @@ export default Ember.Component.extend({ render: _.bind(function(name, type, data) { if(type === 'display' && name && name !== '-') { let link; - let params = _.clone(this.get('queryParamValues')); + let params = _.clone(this.get('presentQueryParamValues')); if(this.get('regionField') === 'request_ip_city') { delete params.region; params.search = 'request_ip_city:"' + data.id + '"'; @@ -54,7 +54,7 @@ export default Ember.Component.extend({ table.draw(); }), - downloadUrl: Ember.computed('allQueryParamValues', function() { - return '/admin/stats/map.csv?' + $.param(this.get('allQueryParamValues')); + downloadUrl: Ember.computed('backendQueryParamValues', function() { + return '/admin/stats/map.csv?' + $.param(this.get('backendQueryParamValues')); }), }); diff --git a/src/api-umbrella/admin-ui/app/components/stats/query-form.js b/src/api-umbrella/admin-ui/app/components/stats/query-form.js index 81d8a59d7..c3de0c1c4 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/query-form.js +++ b/src/api-umbrella/admin-ui/app/components/stats/query-form.js @@ -1,46 +1,36 @@ import Ember from 'ember'; import I18n from 'npm:i18n-js'; +import moment from 'npm:moment-timezone'; +import 'npm:bootstrap-daterangepicker'; export default Ember.Component.extend({ session: Ember.inject.service('session'), enableInterval: false, - datePickerRanges: { - 'Today': [ - moment().startOf('day'), - moment().endOf('day'), - ], - 'Yesterday': [ - moment().subtract(1, 'days'), - moment().subtract(1, 'days').endOf('day'), - ], - 'Last 7 Days': [ - moment().subtract(6, 'days'), - moment().endOf('day'), - ], - 'Last 30 Days': [ - moment().subtract(29, 'days').startOf('day'), - moment().endOf('day'), - ], - 'This Month': [ - moment().startOf('month'), - moment().endOf('month'), - ], - 'Last Month': [ - moment().subtract(1, 'month').startOf('month'), - moment().subtract(1, 'month').endOf('month'), - ], - }, - didInsertElement() { - this.updateDateRange(); + let rangeOptions = {}; + let rangeKeys = {}; + _.forEach(this.get('dateRanges'), function(range, key) { + rangeOptions[range.label] = [ + range.start_at, + range.end_at, + ]; + rangeKeys[range.label] = key; + }); + this.set('rangeOptions', rangeOptions); + this.set('rangeKeys', rangeKeys); - $('#reportrange').daterangepicker({ - ranges: this.datePickerRanges, - startDate: moment(this.get('start_at'), 'YYYY-MM-DD'), - endDate: moment(this.get('end_at'), 'YYYY-MM-DD'), - }, _.bind(this.handleDateRangeChange, this)); + let $dateRangePicker = $('#reportrange'); + $dateRangePicker.daterangepicker({ + ranges: rangeOptions, + }); + $dateRangePicker.on('showCalendar.daterangepicker', this.handleDateRangeCalendarShow.bind(this)); + $dateRangePicker.on('hideCalendar.daterangepicker', this.handleDateRangeCalendarHide.bind(this)); + $dateRangePicker.on('apply.daterangepicker', this.handleDateRangeApply.bind(this)); + + this.dateRangePicker = $dateRangePicker.data('daterangepicker'); + this.updateDateRange(); let stringOperators = [ 'begins_with', @@ -293,18 +283,59 @@ export default Ember.Component.extend({ } }.observes('query'), - updateDateRange: function() { - let start = moment(this.get('start_at')); - let end = moment(this.get('end_at')); + updateDateRange: Ember.observer('allQueryParamValues.start_at', 'allQueryParamValues.end_at', function() { + let start = moment(this.get('allQueryParamValues.start_at'), 'YYYY-MM-DD'); + let end = moment(this.get('allQueryParamValues.end_at'), 'YYYY-MM-DD'); - $('#reportrange span.text').html(start.format('MMM D, YYYY') + ' - ' + end.format('MMM D, YYYY')); - }.observes('start_at', 'end_at'), + this.dateRangePicker.hideCalendars(); + this.dateRangePicker.setStartDate(start); + this.dateRangePicker.setEndDate(end); + $('#reportrange span.text').html(start.format('ll') + ' - ' + end.format('ll')); + }), - handleDateRangeChange(start, end) { - this.setProperties({ - 'start_at': start.format('YYYY-MM-DD'), - 'end_at': end.format('YYYY-MM-DD'), - }); + handleDateRangeCalendarShow() { + this.set('calendarShown', true); + }, + + handleDateRangeCalendarHide() { + this.set('calendarShown', false); + }, + + handleDateRangeApply(event, picker) { + // If the user selects a predefined date range (like "Last 7 Days"), then + // don't set explicit dates in the URL query params. This allows for the + // URLs that are bookmarked or shared to use relative dates (eg, you'll + // always see the last 7 days regardless of when the URL was first + // bookmarked). + // + // If the user selects a custom date range, then explicit dates will be set + // in the URL (so the data is fixed in time). + // + // Note that if the user picks "Custom Range" and happens to select dates + // that correspond with the one of the predefined ranges, then the + // Bootstrap Date Picker sets the "chosenLabel" as if the user picked the + // predefined range. To workaround this issue (so any dates picked when + // "Custom Range" is open are treated the same), we check to see if the + // "Custom Range" calendars are visible or not. + let rangeOptions = this.get('rangeOptions'); + if(rangeOptions[picker.chosenLabel] && !this.get('calendarShown')) { + let rangeKeys = this.get('rangeKeys'); + this.setProperties({ + start_at: '', + end_at: '', + date_range: rangeKeys[picker.chosenLabel], + }); + } else { + this.setProperties({ + start_at: picker.startDate.format('YYYY-MM-DD'), + end_at: picker.endDate.format('YYYY-MM-DD'), + // In this case the "date_range" param isn't being used ("start_at" and + // "end_at" take precedence), so reset it back to the default value + // (defined in app/controllers/stats/base.js), so it's hidden from the + // URL. + date_range: '30d', + }); + } }, actions: { diff --git a/src/api-umbrella/admin-ui/app/components/stats/users/results-table.js b/src/api-umbrella/admin-ui/app/components/stats/users/results-table.js index 6ead7b673..a03010af5 100644 --- a/src/api-umbrella/admin-ui/app/components/stats/users/results-table.js +++ b/src/api-umbrella/admin-ui/app/components/stats/users/results-table.js @@ -10,7 +10,7 @@ export default Ember.Component.extend({ ajax: { url: '/admin/stats/users.json', data: function(data) { - return _.extend({}, data, this.get('allQueryParamValues')); + return _.extend({}, data, this.get('backendQueryParamValues')); }.bind(this), }, order: [[4, 'desc']], @@ -21,7 +21,7 @@ export default Ember.Component.extend({ defaultContent: '-', render: function(email, type, data) { if(type === 'display' && email && email !== '-') { - let params = _.clone(this.get('queryParamValues')); + let params = _.clone(this.get('presentQueryParamValues')); params.search = 'user_id:"' + data.id + '"'; let link = '#/stats/logs?' + $.param(params); @@ -79,11 +79,11 @@ export default Ember.Component.extend({ }); }, - refreshData: Ember.observer('allQueryParamValues', function() { + refreshData: Ember.observer('backendQueryParamValues', function() { this.$().find('table').DataTable().draw(); }), - downloadUrl: Ember.computed('allQueryParamValues', function() { - return '/admin/stats/users.csv?' + $.param(this.get('allQueryParamValues')); + downloadUrl: Ember.computed('backendQueryParamValues', function() { + return '/admin/stats/users.csv?' + $.param(this.get('backendQueryParamValues')); }), }); diff --git a/src/api-umbrella/admin-ui/app/controllers/stats/base.js b/src/api-umbrella/admin-ui/app/controllers/stats/base.js index 6cea91e9a..644a49996 100644 --- a/src/api-umbrella/admin-ui/app/controllers/stats/base.js +++ b/src/api-umbrella/admin-ui/app/controllers/stats/base.js @@ -1,13 +1,13 @@ import Ember from 'ember'; export default Ember.Controller.extend({ - tz: jstz.determine().name(), search: '', interval: 'day', prefix: '0/', region: 'world', - start_at: moment().subtract(29, 'days').format('YYYY-MM-DD'), - end_at: moment().format('YYYY-MM-DD'), + date_range: '30d', + start_at: '', + end_at: '', query: JSON.stringify({ condition: 'AND', rules: [{ diff --git a/src/api-umbrella/admin-ui/app/controllers/stats/drilldown.js b/src/api-umbrella/admin-ui/app/controllers/stats/drilldown.js index aa4a2e3bf..75fb56636 100644 --- a/src/api-umbrella/admin-ui/app/controllers/stats/drilldown.js +++ b/src/api-umbrella/admin-ui/app/controllers/stats/drilldown.js @@ -2,7 +2,6 @@ import Base from './base'; export default Base.extend({ queryParams: [ - 'tz', 'start_at', 'end_at', 'interval', diff --git a/src/api-umbrella/admin-ui/app/controllers/stats/logs.js b/src/api-umbrella/admin-ui/app/controllers/stats/logs.js index c8981d75b..7b618de61 100644 --- a/src/api-umbrella/admin-ui/app/controllers/stats/logs.js +++ b/src/api-umbrella/admin-ui/app/controllers/stats/logs.js @@ -2,7 +2,7 @@ import Base from './base'; export default Base.extend({ queryParams: [ - 'tz', + 'date_range', 'start_at', 'end_at', 'interval', diff --git a/src/api-umbrella/admin-ui/app/controllers/stats/map.js b/src/api-umbrella/admin-ui/app/controllers/stats/map.js index ef5f28b4e..44b0aaa62 100644 --- a/src/api-umbrella/admin-ui/app/controllers/stats/map.js +++ b/src/api-umbrella/admin-ui/app/controllers/stats/map.js @@ -2,7 +2,6 @@ import Base from './base'; export default Base.extend({ queryParams: [ - 'tz', 'start_at', 'end_at', 'query', @@ -10,5 +9,4 @@ export default Base.extend({ 'region', 'beta_analytics', ], - }); diff --git a/src/api-umbrella/admin-ui/app/controllers/stats/users.js b/src/api-umbrella/admin-ui/app/controllers/stats/users.js index e4387a600..9edc03586 100644 --- a/src/api-umbrella/admin-ui/app/controllers/stats/users.js +++ b/src/api-umbrella/admin-ui/app/controllers/stats/users.js @@ -2,7 +2,6 @@ import Base from './base'; export default Base.extend({ queryParams: [ - 'tz', 'start_at', 'end_at', 'query', diff --git a/src/api-umbrella/admin-ui/app/helpers/format-date.js b/src/api-umbrella/admin-ui/app/helpers/format-date.js index 5a8713335..60cb72560 100644 --- a/src/api-umbrella/admin-ui/app/helpers/format-date.js +++ b/src/api-umbrella/admin-ui/app/helpers/format-date.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import moment from 'npm:moment-timezone'; export function formatDate(params) { let date = params[0]; diff --git a/src/api-umbrella/admin-ui/app/models/api/rate-limit.js b/src/api-umbrella/admin-ui/app/models/api/rate-limit.js index 9194ae3ab..6af2d5d0c 100644 --- a/src/api-umbrella/admin-ui/app/models/api/rate-limit.js +++ b/src/api-umbrella/admin-ui/app/models/api/rate-limit.js @@ -1,5 +1,6 @@ import Ember from 'ember'; import DS from 'ember-data'; +import moment from 'npm:moment-timezone'; export default DS.Model.extend({ duration: DS.attr('number'), diff --git a/src/api-umbrella/admin-ui/app/routes/stats/base.js b/src/api-umbrella/admin-ui/app/routes/stats/base.js index 485d7c0e7..be1b1168f 100644 --- a/src/api-umbrella/admin-ui/app/routes/stats/base.js +++ b/src/api-umbrella/admin-ui/app/routes/stats/base.js @@ -1,22 +1,96 @@ import Ember from 'ember'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; +import moment from 'npm:moment-timezone'; export default Ember.Route.extend(AuthenticatedRouteMixin, { setupController(controller, model) { controller.set('model', model); - controller.set('queryParamValues', this.get('queryParamValues') || {}); - controller.set('allQueryParamValues', this.paramsFor(this.routeName)); + controller.set('dateRanges', this.get('dateRanges')); + controller.set('presentQueryParamValues', this.get('presentQueryParamValues') || {}); + controller.set('allQueryParamValues', this.get('allQueryParamValues') || {}); + controller.set('backendQueryParamValues', this.get('backendQueryParamValues') || {}); $('ul.nav li').removeClass('active'); $('ul.nav li.nav-analytics').addClass('active'); }, + beforeModel() { + this._super(...arguments); + + let timezone = this.get('session.data.authenticated.analytics_timezone'); + let dateRanges = { + 'today': { + label: 'Today', + start_at: moment().tz(timezone).startOf('day'), + end_at: moment().tz(timezone).endOf('day'), + }, + 'yesterday': { + label: 'Yesterday', + start_at: moment().tz(timezone).subtract(1, 'days'), + end_at: moment().tz(timezone).subtract(1, 'days').endOf('day'), + }, + '7d': { + label: 'Last 7 Days', + start_at: moment().tz(timezone).subtract(6, 'days'), + end_at: moment().tz(timezone).endOf('day'), + }, + '30d': { + label: 'Last 30 Days', + start_at: moment().tz(timezone).subtract(29, 'days').startOf('day'), + end_at: moment().tz(timezone).endOf('day'), + }, + 'this_month': { + label: 'This Month', + start_at: moment().tz(timezone).startOf('month'), + end_at: moment().tz(timezone).endOf('month'), + }, + 'last_month': { + label: 'Last Month', + start_at: moment().tz(timezone).subtract(1, 'month').startOf('month'), + end_at: moment().tz(timezone).subtract(1, 'month').endOf('month'), + }, + }; + + // If this route has the "date_range" query param set (for dynamic date + // ranges), fill in the "start_at"/"end_at" query params based on the given + // range. But if "start_at" or "end_at" are set, they take precedent. + // + // Most of our other default query params are defined in + // controllers/stats/base.js, but Ember doesn't support dynamic query + // params (see https://github.com/emberjs/ember.js/issues/11592), so this + // is a bit of a workaround. We want dynamic defaults in this case for 2 + // reasons: + // + // 1. So that we can define the default after the session data has been + // fetched and we know what the default analytics timezone is. + // 2. So that the default value changes if the user has the app open for + // multiple days (we don't want the default value from the very first + // load to never be updated again). + let allParams = _.cloneDeep(this.paramsFor(this.routeName) || {}); + if(allParams.date_range) { + let range = dateRanges[allParams.date_range]; + if(range) { + if(!allParams.start_at) { + allParams.start_at = range.start_at.format('YYYY-MM-DD'); + } + if(!allParams.end_at) { + allParams.end_at = range.end_at.format('YYYY-MM-DD'); + } + } + } + + this.set('dateRanges', dateRanges); + this.set('allQueryParamValues', allParams); + this.set('backendQueryParamValues', _.omit(allParams, ['date_range'])); + }, + + validateParams(params) { let valid = true; let interval = params.interval; - let start = moment(params.start_at); - let end = moment(params.end_at); + let start = moment(params.start_at, 'YYYY-MM-DD'); + let end = moment(params.end_at, 'YYYY-MM-DD'); let range = end.unix() - start.unix(); switch(interval) { @@ -44,7 +118,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, { actions: { queryParamsDidChange(changed, present) { this._super(...arguments); - this.set('queryParamValues', present); + this.set('presentQueryParamValues', present); }, }, }); diff --git a/src/api-umbrella/admin-ui/app/routes/stats/drilldown.js b/src/api-umbrella/admin-ui/app/routes/stats/drilldown.js index 2be484eb9..8ee8bf81f 100644 --- a/src/api-umbrella/admin-ui/app/routes/stats/drilldown.js +++ b/src/api-umbrella/admin-ui/app/routes/stats/drilldown.js @@ -3,7 +3,7 @@ import StatsDrilldown from 'api-umbrella-admin-ui/models/stats/drilldown'; export default Base.extend({ queryParams: { - tz: { + date_range: { refreshModel: true, }, start_at: { @@ -29,7 +29,8 @@ export default Base.extend({ }, }, - model(params) { + model() { + let params = this.get('backendQueryParamValues'); if(this.validateParams(params)) { return StatsDrilldown.find(params); } else { diff --git a/src/api-umbrella/admin-ui/app/routes/stats/logs.js b/src/api-umbrella/admin-ui/app/routes/stats/logs.js index 3e9c4c8b9..1d0bc0422 100644 --- a/src/api-umbrella/admin-ui/app/routes/stats/logs.js +++ b/src/api-umbrella/admin-ui/app/routes/stats/logs.js @@ -3,7 +3,7 @@ import StatsLogs from 'api-umbrella-admin-ui/models/stats/logs'; export default Base.extend({ queryParams: { - tz: { + date_range: { refreshModel: true, }, start_at: { @@ -26,7 +26,8 @@ export default Base.extend({ }, }, - model(params) { + model() { + let params = this.get('backendQueryParamValues'); if(this.validateParams(params)) { return StatsLogs.find(params); } else { diff --git a/src/api-umbrella/admin-ui/app/routes/stats/map.js b/src/api-umbrella/admin-ui/app/routes/stats/map.js index cfc4fb4ea..2cbbd920f 100644 --- a/src/api-umbrella/admin-ui/app/routes/stats/map.js +++ b/src/api-umbrella/admin-ui/app/routes/stats/map.js @@ -3,7 +3,7 @@ import StatsMap from 'api-umbrella-admin-ui/models/stats/map'; export default Base.extend({ queryParams: { - tz: { + date_range: { refreshModel: true, }, start_at: { @@ -26,7 +26,8 @@ export default Base.extend({ }, }, - model(params) { + model() { + let params = this.get('backendQueryParamValues'); if(this.validateParams(params)) { return StatsMap.find(params); } else { diff --git a/src/api-umbrella/admin-ui/app/routes/stats/users.js b/src/api-umbrella/admin-ui/app/routes/stats/users.js index 78720a6ad..e510e3c47 100644 --- a/src/api-umbrella/admin-ui/app/routes/stats/users.js +++ b/src/api-umbrella/admin-ui/app/routes/stats/users.js @@ -2,7 +2,7 @@ import Base from './base'; export default Base.extend({ queryParams: { - tz: { + date_range: { refreshModel: true, }, start_at: { @@ -21,4 +21,8 @@ export default Base.extend({ refreshModel: true, }, }, + + model() { + return {}; + }, }); diff --git a/src/api-umbrella/admin-ui/app/styles/app.scss b/src/api-umbrella/admin-ui/app/styles/app.scss index f09fbf818..9416c3ce3 100644 --- a/src/api-umbrella/admin-ui/app/styles/app.scss +++ b/src/api-umbrella/admin-ui/app/styles/app.scss @@ -6,7 +6,7 @@ @import "bower_components/font-awesome/scss/font-awesome.scss"; @import "bower_components/jQuery-QueryBuilder/dist/scss/default.scss"; -@import "bower_components/bootstrap-daterangepicker/daterangepicker.scss"; +@import "node_modules/bootstrap-daterangepicker/daterangepicker.scss"; @import "_noscript.scss"; @import "_busy-blocker.scss"; @import "_input-xs.scss"; diff --git a/src/api-umbrella/admin-ui/app/templates/components/stats/logs/results-highlights.hbs b/src/api-umbrella/admin-ui/app/templates/components/stats/logs/results-highlights.hbs index d132c13e0..729418cb0 100644 --- a/src/api-umbrella/admin-ui/app/templates/components/stats/logs/results-highlights.hbs +++ b/src/api-umbrella/admin-ui/app/templates/components/stats/logs/results-highlights.hbs @@ -7,13 +7,13 @@ {{format-number stats.total_users format="0,0"}} unique {{inflect "user" stats.total_users}} - {{stats/logs/results-facet-table label="view top users" field="user_email" facets=aggregations.users queryParamValues=queryParamValues}} + {{stats/logs/results-facet-table label="view top users" field="user_email" facets=aggregations.users presentQueryParamValues=presentQueryParamValues}}
{{format-number stats.total_ips format="0,0"}} unique ip {{inflect "address" stats.total_ips}} - {{stats/logs/results-facet-table label="view top ips" field="request_ip" facets=aggregations.ips queryParamValues=queryParamValues}} + {{stats/logs/results-facet-table label="view top ips" field="request_ip" facets=aggregations.ips presentQueryParamValues=presentQueryParamValues}}
{{format-number stats.average_response_time format="0,0"}} ms diff --git a/src/api-umbrella/admin-ui/app/templates/stats/drilldown.hbs b/src/api-umbrella/admin-ui/app/templates/stats/drilldown.hbs index 11a16cce4..342c6874f 100644 --- a/src/api-umbrella/admin-ui/app/templates/stats/drilldown.hbs +++ b/src/api-umbrella/admin-ui/app/templates/stats/drilldown.hbs @@ -1,4 +1,4 @@ -{{stats/query-form enableInterval=true start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics}} +{{stats/query-form enableInterval=true date_range=date_range start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics allQueryParamValues=allQueryParamValues dateRanges=dateRanges}} {{stats/drilldown/results-chart hitsOverTime=model.hits_over_time}} {{stats/drilldown/results-breadcrumbs breadcrumbs=model.breadcrumbs}} -{{stats/drilldown/results-table results=model.results queryParamValues=queryParamValues allQueryParamValues=allQueryParamValues}} +{{stats/drilldown/results-table results=model.results presentQueryParamValues=presentQueryParamValues backendQueryParamValues=backendQueryParamValues}} diff --git a/src/api-umbrella/admin-ui/app/templates/stats/logs.hbs b/src/api-umbrella/admin-ui/app/templates/stats/logs.hbs index 5bfd549ac..e96aa5926 100644 --- a/src/api-umbrella/admin-ui/app/templates/stats/logs.hbs +++ b/src/api-umbrella/admin-ui/app/templates/stats/logs.hbs @@ -1,4 +1,4 @@ -{{stats/query-form enableInterval=true start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics}} +{{stats/query-form enableInterval=true date_range=date_range start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics allQueryParamValues=allQueryParamValues dateRanges=dateRanges}} {{stats/logs/results-chart hitsOverTime=model.hits_over_time}} -{{stats/logs/results-highlights stats=model.stats aggregations=model.aggregations queryParamValues=queryParamValues}} -{{stats/logs/results-table queryParamValues=queryParamValues allQueryParamValues=allQueryParamValues}} +{{stats/logs/results-highlights stats=model.stats aggregations=model.aggregations presentQueryParamValues=presentQueryParamValues}} +{{stats/logs/results-table presentQueryParamValues=presentQueryParamValues backendQueryParamValues=backendQueryParamValues}} diff --git a/src/api-umbrella/admin-ui/app/templates/stats/map.hbs b/src/api-umbrella/admin-ui/app/templates/stats/map.hbs index 6aad7c3c9..f34d0dc1c 100644 --- a/src/api-umbrella/admin-ui/app/templates/stats/map.hbs +++ b/src/api-umbrella/admin-ui/app/templates/stats/map.hbs @@ -1,6 +1,6 @@ -{{stats/query-form enableInterval=false start_at=start_at end_at=end_at query=query search=search region=region beta_analytics=beta_analytics}} +{{stats/query-form enableInterval=false date_range=date_range start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics allQueryParamValues=allQueryParamValues dateRanges=dateRanges}}
{{stats/map/results-breadcrumbs breadcrumbs=model.map_breadcrumbs}} - {{stats/map/results-map regions=model.map_regions regionField=model.region_field queryParamValues=queryParamValues allQueryParamValues=allQueryParamValues}} + {{stats/map/results-map regions=model.map_regions regionField=model.region_field presentQueryParamValues=presentQueryParamValues allQueryParamValues=allQueryParamValues}}
-{{stats/map/results-table regions=model.regions regionField=model.region_field queryParamValues=queryParamValues allQueryParamValues=allQueryParamValues}} +{{stats/map/results-table regions=model.regions regionField=model.region_field presentQueryParamValues=presentQueryParamValues backendQueryParamValues=backendQueryParamValues}} diff --git a/src/api-umbrella/admin-ui/app/templates/stats/users.hbs b/src/api-umbrella/admin-ui/app/templates/stats/users.hbs index 67af79a11..7c5e242ad 100644 --- a/src/api-umbrella/admin-ui/app/templates/stats/users.hbs +++ b/src/api-umbrella/admin-ui/app/templates/stats/users.hbs @@ -1,2 +1,2 @@ -{{stats/query-form enableInterval=false start_at=start_at end_at=end_at query=query search=search beta_analytics=beta_analytics}} -{{stats/users/results-table queryParamValues=queryParamValues allQueryParamValues=allQueryParamValues}} +{{stats/query-form enableInterval=false date_range=date_range start_at=start_at end_at=end_at query=query search=search interval=interval beta_analytics=beta_analytics allQueryParamValues=allQueryParamValues dateRanges=dateRanges}} +{{stats/users/results-table presentQueryParamValues=presentQueryParamValues backendQueryParamValues=backendQueryParamValues}} diff --git a/src/api-umbrella/admin-ui/app/utils/data-tables-helpers.js b/src/api-umbrella/admin-ui/app/utils/data-tables-helpers.js index 2e0fcfde8..bb7477d6d 100644 --- a/src/api-umbrella/admin-ui/app/utils/data-tables-helpers.js +++ b/src/api-umbrella/admin-ui/app/utils/data-tables-helpers.js @@ -1,3 +1,5 @@ +import moment from 'npm:moment-timezone'; + export default { renderEscaped(value, type) { if(type === 'display' && value) { diff --git a/src/api-umbrella/admin-ui/bower.json b/src/api-umbrella/admin-ui/bower.json index 6a0b935d5..769646119 100644 --- a/src/api-umbrella/admin-ui/bower.json +++ b/src/api-umbrella/admin-ui/bower.json @@ -4,7 +4,6 @@ "ace-builds": "1.2.6", "bootbox": "4.4.0", "bootstrap": "3.3.7", - "bootstrap-daterangepicker": "2.1.25", "bootstrap-sass": "3.3.7", "bootswatch": "3.3.7", "datatables": "1.10.13", @@ -17,10 +16,8 @@ "jquery-ui": "1.12.1", "jquery.scrollTo": "2.1.2", "jsdiff": "3.2.0", - "jstz-detect": "1.0.5", "lodash": "4.17.4", "marked": "0.3.6", - "moment": "2.17.1", "pnotify": "3.0.0", "qtip2": "2.2.1", "selectize": "0.12.4", diff --git a/src/api-umbrella/admin-ui/ember-cli-build.js b/src/api-umbrella/admin-ui/ember-cli-build.js index 7bd4c2a4d..b9be4b49e 100644 --- a/src/api-umbrella/admin-ui/ember-cli-build.js +++ b/src/api-umbrella/admin-ui/ember-cli-build.js @@ -59,10 +59,8 @@ module.exports = function(defaults) { app.import('bower_components/jquery-bbq-deparam/jquery-deparam.js'); app.import('bower_components/jquery.scrollTo/jquery.scrollTo.js'); app.import('bower_components/jsdiff/diff.js'); - app.import('bower_components/jstz-detect/jstz.js'); app.import('bower_components/lodash/dist/lodash.js'); app.import('bower_components/marked/lib/marked.js'); - app.import('bower_components/moment/moment.js'); app.import('bower_components/pnotify/dist/pnotify.css'); app.import('bower_components/pnotify/dist/pnotify.buttons.css'); app.import('bower_components/pnotify/dist/pnotify.mobile.css'); @@ -82,7 +80,5 @@ module.exports = function(defaults) { app.import('bower_components/jquery-ui/ui/widgets/sortable.js'); app.import('bower_components/tbasse-jquery-truncate/jquery.truncate.js'); - app.import('bower_components/bootstrap-daterangepicker/daterangepicker.js'); - return app.toTree(); }; diff --git a/src/api-umbrella/admin-ui/package.json b/src/api-umbrella/admin-ui/package.json index a0b8e7cb5..9b83d233b 100644 --- a/src/api-umbrella/admin-ui/package.json +++ b/src/api-umbrella/admin-ui/package.json @@ -20,6 +20,7 @@ "license": "MIT", "devDependencies": { "bower": "~1.8.0", + "bootstrap-daterangepicker": "~2.1.25", "broccoli-asset-rev": "^2.4.2", "echarts": "~3.4.0", "ember-bootstrap": "~0.11.2", @@ -50,7 +51,8 @@ "ember-truth-helpers": "~1.3.0", "emberx-select": "~3.0.0", "i18n-js": "http://github.com/fnando/i18n-js/archive/v3.0.0.rc15.tar.gz", - "loader.js": "^4.0.1" + "loader.js": "^4.0.1", + "moment-timezone": "~0.5.11" }, "ember-addon": { "paths": [ diff --git a/src/api-umbrella/admin-ui/yarn.lock b/src/api-umbrella/admin-ui/yarn.lock index 60d0199ff..b814c4dfc 100644 --- a/src/api-umbrella/admin-ui/yarn.lock +++ b/src/api-umbrella/admin-ui/yarn.lock @@ -55,8 +55,8 @@ ajv-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" ajv@^4.7.0: - version "4.11.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.2.tgz#f166c3c11cbc6cb9dcc102a5bcfe5b72c95287e6" + version "4.11.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.3.tgz#ce30bdb90d1254f762c75af915fb3a63e7183d22" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -877,7 +877,7 @@ broccoli-stew@^1.0.4, broccoli-stew@^1.2.0, broccoli-stew@^1.3.3: broccoli-templater@^1.0.0: version "1.0.0" - resolved "http://registry.npmjs.org/broccoli-templater/-/broccoli-templater-1.0.0.tgz#7c054aacf596d1868d1a44291f9ec7b907d30ecf" + resolved "https://registry.yarnpkg.com/broccoli-templater/-/broccoli-templater-1.0.0.tgz#7c054aacf596d1868d1a44291f9ec7b907d30ecf" dependencies: broccoli-filter "^0.1.11" broccoli-stew "^1.2.0" @@ -1420,11 +1420,11 @@ configstore@^2.0.0: xdg-basedir "^2.0.0" connect@^3.3.3: - version "3.5.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" + version "3.5.1" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.1.tgz#6d30d7a63c7f170857a6b3aa6b363d973dca588e" dependencies: debug "~2.2.0" - finalhandler "0.5.0" + finalhandler "0.5.1" parseurl "~1.3.1" utils-merge "1.0.0" @@ -2773,16 +2773,6 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" - dependencies: - debug "~2.2.0" - escape-html "~1.0.3" - on-finished "~2.3.0" - statuses "~1.3.0" - unpipe "~1.0.0" - finalhandler@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.1.tgz#2c400d8d4530935bc232549c5fa385ec07de6fcd" @@ -3650,8 +3640,8 @@ js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.5.1: esprima "^3.1.1" jsbn@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" jsesc@~0.5.0: version "0.5.0" @@ -4287,6 +4277,16 @@ module-deps@^4.0.8: through2 "^2.0.0" xtend "^4.0.0" +moment-timezone@~0.5.11: + version "0.5.11" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.11.tgz#9b76c03d8ef514c7e4249a7bbce649eed39ef29f" + dependencies: + moment ">= 2.6.0" + +"moment@>= 2.6.0": + version "2.17.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" + morgan@^1.5.2: version "1.8.1" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.8.1.tgz#f93023d3887bd27b78dfd6023cea7892ee27a4b1" @@ -5196,7 +5196,7 @@ regenerator@0.8.40: regex-cache@^0.4.2: version "0.4.3" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + resolved "http://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" dependencies: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" @@ -5674,7 +5674,7 @@ stable@~0.1.3: version "0.1.5" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" -statuses@1, "statuses@>= 1.3.1 < 2", statuses@~1.3.0, statuses@~1.3.1: +statuses@1, "statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -6045,7 +6045,7 @@ umask@~1.1.0: umd@^3.0.0: version "3.0.1" - resolved "http://registry.npmjs.org/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" underscore.string@~2.3.3: version "2.3.3" diff --git a/src/api-umbrella/web-app/app/controllers/admin/sessions_controller.rb b/src/api-umbrella/web-app/app/controllers/admin/sessions_controller.rb index 0e30f456d..d0058b84d 100644 --- a/src/api-umbrella/web-app/app/controllers/admin/sessions_controller.rb +++ b/src/api-umbrella/web-app/app/controllers/admin/sessions_controller.rb @@ -16,6 +16,7 @@ def auth if current_admin response.merge!({ + "analytics_timezone" => ApiUmbrellaConfig[:analytics][:timezone], "enable_beta_analytics" => (ApiUmbrellaConfig[:analytics][:adapter] == "kylin" || (ApiUmbrellaConfig[:analytics][:outputs] && ApiUmbrellaConfig[:analytics][:outputs].include?("kylin"))), "username_is_email" => ApiUmbrellaConfig[:web][:admin][:username_is_email], "local_auth_enabled" => ApiUmbrellaConfig[:web][:admin][:auth_strategies][:_local_enabled?], diff --git a/test/admin_ui/test_legacy_redirects.rb b/test/admin_ui/test_legacy_redirects.rb index 2c206ee51..54e2c93ce 100644 --- a/test/admin_ui/test_legacy_redirects.rb +++ b/test/admin_ui/test_legacy_redirects.rb @@ -19,18 +19,12 @@ def test_drilldown visit "/admin/#/stats/drilldown/tz=America%2FDenver&search=&start_at=2015-01-15&end_at=2015-01-18&query=%7B%22condition%22%3A%22AND%22%2C%22rules%22%3A%5B%7B%22id%22%3A%22gatekeeper_denied_code%22%2C%22field%22%3A%22gatekeeper_denied_code%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22select%22%2C%22operator%22%3A%22is_null%22%2C%22value%22%3Anull%7D%2C%7B%22id%22%3A%22request_host%22%2C%22field%22%3A%22request_host%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22text%22%2C%22operator%22%3A%22begins_with%22%2C%22value%22%3A%22example.com%22%7D%5D%7D&interval=hour&beta_analytics=false®ion=US" assert_link("Download CSV", :href => /start_at=2015-01-15/) - uri = Addressable::URI.parse(page.current_url) - assert_equal("/admin/", uri.path) - assert(uri.fragment) - - fragment_uri = Addressable::URI.parse(uri.fragment) - assert_equal("/stats/drilldown", fragment_uri.path) - assert_equal({ + assert_current_admin_url("/stats/drilldown", { "start_at" => "2015-01-15", "end_at" => "2015-01-18", "interval" => "hour", "query" => "{\"condition\":\"AND\",\"rules\":[{\"id\":\"gatekeeper_denied_code\",\"field\":\"gatekeeper_denied_code\",\"type\":\"string\",\"input\":\"select\",\"operator\":\"is_null\",\"value\":null},{\"id\":\"request_host\",\"field\":\"request_host\",\"type\":\"string\",\"input\":\"text\",\"operator\":\"begins_with\",\"value\":\"example.com\"}]}", - }.merge(expected_tz_param), fragment_uri.query_values) + }) end def test_logs @@ -38,18 +32,12 @@ def test_logs visit "/admin/#/stats/logs/tz=America%2FDenver&search=&start_at=2015-01-15&end_at=2015-01-18&query=%7B%22condition%22%3A%22AND%22%2C%22rules%22%3A%5B%7B%22id%22%3A%22gatekeeper_denied_code%22%2C%22field%22%3A%22gatekeeper_denied_code%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22select%22%2C%22operator%22%3A%22is_null%22%2C%22value%22%3Anull%7D%2C%7B%22id%22%3A%22request_host%22%2C%22field%22%3A%22request_host%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22text%22%2C%22operator%22%3A%22begins_with%22%2C%22value%22%3A%22example.com%22%7D%5D%7D&interval=hour&beta_analytics=false®ion=US" assert_link("Download CSV", :href => /start_at=2015-01-15/) - uri = Addressable::URI.parse(page.current_url) - assert_equal("/admin/", uri.path) - assert(uri.fragment) - - fragment_uri = Addressable::URI.parse(uri.fragment) - assert_equal("/stats/logs", fragment_uri.path) - assert_equal({ + assert_current_admin_url("/stats/logs", { "start_at" => "2015-01-15", "end_at" => "2015-01-18", "interval" => "hour", "query" => "{\"condition\":\"AND\",\"rules\":[{\"id\":\"gatekeeper_denied_code\",\"field\":\"gatekeeper_denied_code\",\"type\":\"string\",\"input\":\"select\",\"operator\":\"is_null\",\"value\":null},{\"id\":\"request_host\",\"field\":\"request_host\",\"type\":\"string\",\"input\":\"text\",\"operator\":\"begins_with\",\"value\":\"example.com\"}]}", - }.merge(expected_tz_param), fragment_uri.query_values) + }) end def test_users @@ -57,17 +45,11 @@ def test_users visit "/admin/#/stats/users/tz=America%2FDenver&search=&start_at=2015-01-15&end_at=2015-01-18&query=%7B%22condition%22%3A%22AND%22%2C%22rules%22%3A%5B%7B%22id%22%3A%22gatekeeper_denied_code%22%2C%22field%22%3A%22gatekeeper_denied_code%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22select%22%2C%22operator%22%3A%22is_null%22%2C%22value%22%3Anull%7D%2C%7B%22id%22%3A%22request_host%22%2C%22field%22%3A%22request_host%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22text%22%2C%22operator%22%3A%22begins_with%22%2C%22value%22%3A%22example.com%22%7D%5D%7D&interval=hour&beta_analytics=false®ion=US" assert_link("Download CSV", :href => /start_at=2015-01-15/) - uri = Addressable::URI.parse(page.current_url) - assert_equal("/admin/", uri.path) - assert(uri.fragment) - - fragment_uri = Addressable::URI.parse(uri.fragment) - assert_equal("/stats/users", fragment_uri.path) - assert_equal({ + assert_current_admin_url("/stats/users", { "start_at" => "2015-01-15", "end_at" => "2015-01-18", "query" => "{\"condition\":\"AND\",\"rules\":[{\"id\":\"gatekeeper_denied_code\",\"field\":\"gatekeeper_denied_code\",\"type\":\"string\",\"input\":\"select\",\"operator\":\"is_null\",\"value\":null},{\"id\":\"request_host\",\"field\":\"request_host\",\"type\":\"string\",\"input\":\"text\",\"operator\":\"begins_with\",\"value\":\"example.com\"}]}", - }.merge(expected_tz_param), fragment_uri.query_values) + }) end def test_map @@ -75,32 +57,11 @@ def test_map visit "/admin/#/stats/map/tz=America%2FDenver&search=&start_at=2015-01-15&end_at=2015-01-18&query=%7B%22condition%22%3A%22AND%22%2C%22rules%22%3A%5B%7B%22id%22%3A%22gatekeeper_denied_code%22%2C%22field%22%3A%22gatekeeper_denied_code%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22select%22%2C%22operator%22%3A%22is_null%22%2C%22value%22%3Anull%7D%2C%7B%22id%22%3A%22request_host%22%2C%22field%22%3A%22request_host%22%2C%22type%22%3A%22string%22%2C%22input%22%3A%22text%22%2C%22operator%22%3A%22begins_with%22%2C%22value%22%3A%22example.com%22%7D%5D%7D&interval=hour&beta_analytics=false®ion=US" assert_link("Download CSV", :href => /start_at=2015-01-15/) - uri = Addressable::URI.parse(page.current_url) - assert_equal("/admin/", uri.path) - assert(uri.fragment) - - fragment_uri = Addressable::URI.parse(uri.fragment) - assert_equal("/stats/map", fragment_uri.path) - assert_equal({ + assert_current_admin_url("/stats/map", { "start_at" => "2015-01-15", "end_at" => "2015-01-18", "query" => "{\"condition\":\"AND\",\"rules\":[{\"id\":\"gatekeeper_denied_code\",\"field\":\"gatekeeper_denied_code\",\"type\":\"string\",\"input\":\"select\",\"operator\":\"is_null\",\"value\":null},{\"id\":\"request_host\",\"field\":\"request_host\",\"type\":\"string\",\"input\":\"text\",\"operator\":\"begins_with\",\"value\":\"example.com\"}]}", "region" => "US", - }.merge(expected_tz_param), fragment_uri.query_values) - end - - private - - def expected_tz_param - # In all our redirect tests, we pass in "America/Denver" for the timezone - # parameter. If the user's current timezone happens to be America/Denver, - # then the "tz" parameter won't be in the redirect (since Ember doesn't add - # default parameters into the URL). However, if we're running in any other - # timezone, this non-default "tz" parameter should be part of the redirect. - if(ENV["TZ"] == "America/Denver") - {} - else - { "tz" => "America/Denver" } - end + }) end end diff --git a/test/admin_ui/test_stats_drilldown.rb b/test/admin_ui/test_stats_drilldown.rb index 874864b5b..8aee1d456 100644 --- a/test/admin_ui/test_stats_drilldown.rb +++ b/test/admin_ui/test_stats_drilldown.rb @@ -3,6 +3,7 @@ class Test::AdminUi::TestStatsDrilldown < Minitest::Capybara::Test include Capybara::Screenshot::MiniTestPlugin include ApiUmbrellaTestHelpers::AdminAuth + include ApiUmbrellaTestHelpers::DateRangePicker include ApiUmbrellaTestHelpers::Setup def setup @@ -28,7 +29,7 @@ def test_csv_download }) admin_login - visit "/admin/#/stats/drilldown?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/drilldown?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-12/) @@ -36,7 +37,6 @@ def test_csv_download uri = Addressable::URI.parse(link[:href]) assert_equal("/api-umbrella/v1/analytics/drilldown.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "start_at" => "2015-01-12", "end_at" => "2015-01-18", "interval" => "day", @@ -64,4 +64,8 @@ def test_csv_download assert_equal(200, page.status_code) assert_equal("text/csv", page.response_headers["Content-Type"]) end + + def test_date_range_picker + assert_date_range_picker("/stats/drilldown") + end end diff --git a/test/admin_ui/test_stats_logs.rb b/test/admin_ui/test_stats_logs.rb index 2e0839f6a..ca421ea63 100644 --- a/test/admin_ui/test_stats_logs.rb +++ b/test/admin_ui/test_stats_logs.rb @@ -3,6 +3,7 @@ class Test::AdminUi::TestStatsLogs < Minitest::Capybara::Test include Capybara::Screenshot::MiniTestPlugin include ApiUmbrellaTestHelpers::AdminAuth + include ApiUmbrellaTestHelpers::DateRangePicker include ApiUmbrellaTestHelpers::Setup def setup @@ -16,7 +17,7 @@ def test_xss_escaping_in_table LogItem.gateway.refresh_index! admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_text(log.request_method) @@ -46,14 +47,13 @@ def test_csv_download_link_changes_with_filters }) admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-12/) link = find_link("Download CSV") uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/logs.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "search" => "", "start_at" => "2015-01-12", "end_at" => "2015-01-18", @@ -62,14 +62,13 @@ def test_csv_download_link_changes_with_filters "beta_analytics" => "false", }, uri.query_values) - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-13&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-13&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-13/) link = find_link("Download CSV") uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/logs.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "search" => "", "start_at" => "2015-01-13", "end_at" => "2015-01-18", @@ -78,7 +77,7 @@ def test_csv_download_link_changes_with_filters "beta_analytics" => "false", }, uri.query_values) - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-12/) assert_link("Download CSV", :href => /#{Regexp.escape(CGI.escape('"rules":[{'))}/) @@ -90,7 +89,6 @@ def test_csv_download_link_changes_with_filters uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/logs.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "start_at" => "2015-01-12", "end_at" => "2015-01-18", "interval" => "day", @@ -99,7 +97,7 @@ def test_csv_download_link_changes_with_filters "beta_analytics" => "false", }, uri.query_values) - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-13&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-13&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-13/) find("a", :text => /Switch to advanced filters/).click @@ -111,7 +109,6 @@ def test_csv_download_link_changes_with_filters uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/logs.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "search" => "response_status:200", "start_at" => "2015-01-13", "end_at" => "2015-01-18", @@ -127,7 +124,7 @@ def test_csv_download LogItem.gateway.refresh_index! admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" refute_selector(".busy-blocker") # Wait for the ajax actions to fetch the graph and tables to both @@ -150,7 +147,7 @@ def test_csv_download def test_changing_intervals admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-13&interval=week" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-13&interval=week" refute_selector(".busy-blocker") assert_selector("button.active", :text => "Week") assert_link("Download CSV", :href => /interval=week/) @@ -178,7 +175,7 @@ def test_changing_intervals def test_does_not_show_beta_analytics_toggle_by_default admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" assert_text("view top users") refute_text("Beta Analytics") end @@ -186,9 +183,13 @@ def test_does_not_show_beta_analytics_toggle_by_default def test_shows_beta_analytics_toggle_when_enabled override_config({ "analytics" => { "outputs" => ["kylin"] } }, nil) do admin_login - visit "/admin/#/stats/logs?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" + visit "/admin/#/stats/logs?search=&start_at=2015-01-12&end_at=2015-01-18&interval=day" assert_text("view top users") assert_text("Beta Analytics") end end + + def test_date_range_picker + assert_date_range_picker("/stats/logs") + end end diff --git a/test/admin_ui/test_stats_map.rb b/test/admin_ui/test_stats_map.rb index 069555a24..a8e1fcfe7 100644 --- a/test/admin_ui/test_stats_map.rb +++ b/test/admin_ui/test_stats_map.rb @@ -3,6 +3,7 @@ class Test::AdminUi::TestStatsMap < Minitest::Capybara::Test include Capybara::Screenshot::MiniTestPlugin include ApiUmbrellaTestHelpers::AdminAuth + include ApiUmbrellaTestHelpers::DateRangePicker include ApiUmbrellaTestHelpers::Setup def setup @@ -28,7 +29,7 @@ def test_csv_download }) admin_login - visit "/admin/#/stats/map?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18" + visit "/admin/#/stats/map?search=&start_at=2015-01-12&end_at=2015-01-18" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-12/) @@ -36,7 +37,6 @@ def test_csv_download uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/map.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "start_at" => "2015-01-12", "end_at" => "2015-01-18", "search" => "", @@ -63,4 +63,8 @@ def test_csv_download assert_equal(200, page.status_code) assert_equal("text/csv", page.response_headers["Content-Type"]) end + + def test_date_range_picker + assert_date_range_picker("/stats/map") + end end diff --git a/test/admin_ui/test_stats_users.rb b/test/admin_ui/test_stats_users.rb index 5e911a70c..f98395d99 100644 --- a/test/admin_ui/test_stats_users.rb +++ b/test/admin_ui/test_stats_users.rb @@ -3,6 +3,7 @@ class Test::AdminUi::TestStatsUsers < Minitest::Capybara::Test include Capybara::Screenshot::MiniTestPlugin include ApiUmbrellaTestHelpers::AdminAuth + include ApiUmbrellaTestHelpers::DateRangePicker include ApiUmbrellaTestHelpers::Setup def setup @@ -23,7 +24,7 @@ def test_xss_escaping_in_table LogItem.gateway.refresh_index! admin_login - visit "/admin/#/stats/users?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18" + visit "/admin/#/stats/users?search=&start_at=2015-01-12&end_at=2015-01-18" refute_selector(".busy-blocker") assert_text(user.email) @@ -57,7 +58,7 @@ def test_csv_download }) admin_login - visit "/admin/#/stats/users?tz=America%2FDenver&search=&start_at=2015-01-12&end_at=2015-01-18" + visit "/admin/#/stats/users?search=&start_at=2015-01-12&end_at=2015-01-18" refute_selector(".busy-blocker") assert_link("Download CSV", :href => /start_at=2015-01-12/) @@ -65,7 +66,6 @@ def test_csv_download uri = Addressable::URI.parse(link[:href]) assert_equal("/admin/stats/users.csv", uri.path) assert_equal({ - "tz" => "America/Denver", "start_at" => "2015-01-12", "end_at" => "2015-01-18", "search" => "", @@ -90,4 +90,8 @@ def test_csv_download assert_equal(200, page.status_code) assert_equal("text/csv", page.response_headers["Content-Type"]) end + + def test_date_range_picker + assert_date_range_picker("/stats/users") + end end diff --git a/test/apis/admin/stats/test_logs.rb b/test/apis/admin/stats/test_logs.rb index 0cd3d07bb..ec5a60e3f 100644 --- a/test/apis/admin/stats/test_logs.rb +++ b/test/apis/admin/stats/test_logs.rb @@ -16,7 +16,6 @@ def test_strips_api_keys_from_request_url_in_json response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "interval" => "day", @@ -41,7 +40,6 @@ def test_strips_api_keys_from_request_url_in_csv response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.csv", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "interval" => "day", @@ -62,7 +60,6 @@ def test_downloading_csv_that_uses_scan_and_scroll_elasticsearch_query response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.csv", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "search" => "", "start_at" => "2015-01-13", "end_at" => "2015-01-18", @@ -85,7 +82,6 @@ def test_query_builder_case_insensitive_defaults response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "interval" => "day", @@ -107,7 +103,6 @@ def test_query_builder_api_key_case_sensitive response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "interval" => "day", @@ -130,7 +125,6 @@ def test_query_builder_nulls response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/logs.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "interval" => "day", diff --git a/test/apis/admin/stats/test_map.rb b/test/apis/admin/stats/test_map.rb index 0ecfe1325..066d48ffd 100644 --- a/test/apis/admin/stats/test_map.rb +++ b/test/apis/admin/stats/test_map.rb @@ -17,7 +17,6 @@ def test_world response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/map.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "region" => "world", @@ -50,7 +49,6 @@ def test_country_non_us response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/map.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "region" => "CA", @@ -84,7 +82,6 @@ def test_country_us response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/map.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "region" => "US", @@ -119,7 +116,6 @@ def test_us_state response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/map.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - "tz" => "America/Denver", "start_at" => "2015-01-13", "end_at" => "2015-01-18", "region" => "US-CO", diff --git a/test/apis/admin/stats/test_search.rb b/test/apis/admin/stats/test_search.rb index ef5798107..d8fa3acad 100644 --- a/test/apis/admin/stats/test_search.rb +++ b/test/apis/admin/stats/test_search.rb @@ -21,7 +21,6 @@ def test_bins_results_by_day_with_time_zone_support response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/search.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -55,7 +54,6 @@ def test_bins_daily_results_daylight_saving_time_begin response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/search.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-03-07", :end_at => "2015-03-09", @@ -91,7 +89,6 @@ def test_bins_hourly_results_daylight_saving_time_begin response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/search.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-03-08", :end_at => "2015-03-08", @@ -133,7 +130,6 @@ def test_bins_daily_results_daylight_saving_time_end response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/search.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2014-11-01", :end_at => "2014-11-03", @@ -169,7 +165,6 @@ def test_bins_hourly_results_daylight_saving_time_end response = Typhoeus.get("https://127.0.0.1:9081/admin/stats/search.json", http_options.deep_merge(admin_session).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2014-11-02", :end_at => "2014-11-02", diff --git a/test/apis/admin/test_auth.rb b/test/apis/admin/test_auth.rb index d8d4ee0e1..60cd12119 100644 --- a/test/apis/admin/test_auth.rb +++ b/test/apis/admin/test_auth.rb @@ -32,6 +32,7 @@ def test_authenticated assert_equal([ "admin", "admin_auth_token", + "analytics_timezone", "api_key", "api_umbrella_version", "authenticated", @@ -43,6 +44,7 @@ def test_authenticated assert_kind_of(Hash, data["admin"]) assert_kind_of(String, data["admin_auth_token"]) + assert_kind_of(String, data["analytics_timezone"]) assert_kind_of(String, data["api_key"]) assert_kind_of(String, data["api_umbrella_version"]) assert_includes([TrueClass, FalseClass], data["authenticated"].class) diff --git a/test/apis/v1/analytics/test_drilldown.rb b/test/apis/v1/analytics/test_drilldown.rb index 9862451cc..394a5c9fe 100644 --- a/test/apis/v1/analytics/test_drilldown.rb +++ b/test/apis/v1/analytics/test_drilldown.rb @@ -17,7 +17,6 @@ def test_level0_prefix response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -56,7 +55,6 @@ def test_level1_prefix response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -97,7 +95,6 @@ def test_prefix_not_contains response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -141,7 +138,6 @@ def test_prefix_regex_escaping response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -189,7 +185,6 @@ def test_all_results_top_10_for_chart response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -257,7 +252,6 @@ def test_time_zone response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-01-13", :end_at => "2015-01-18", @@ -293,7 +287,6 @@ def test_bins_daily_results_daylight_saving_time_begin response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-03-07", :end_at => "2015-03-09", @@ -331,7 +324,6 @@ def test_bins_hourly_results_daylight_saving_time_begin response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2015-03-08", :end_at => "2015-03-08", @@ -375,7 +367,6 @@ def test_bins_daily_results_daylight_saving_time_end response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2014-11-01", :end_at => "2014-11-03", @@ -413,7 +404,6 @@ def test_bins_hourly_results_daylight_saving_time_end response = Typhoeus.get("https://127.0.0.1:9081/api-umbrella/v1/analytics/drilldown.json", http_options.deep_merge(admin_token).deep_merge({ :params => { - :tz => "America/Denver", :search => "", :start_at => "2014-11-02", :end_at => "2014-11-02", diff --git a/test/support/api_umbrella_test_helpers/admin_auth.rb b/test/support/api_umbrella_test_helpers/admin_auth.rb index 2d46991cb..d0c242f5d 100644 --- a/test/support/api_umbrella_test_helpers/admin_auth.rb +++ b/test/support/api_umbrella_test_helpers/admin_auth.rb @@ -143,6 +143,20 @@ def make_first_time_admin_creation_requests [get_response, create_response] end + def assert_current_admin_url(fragment_path, fragment_query_values) + uri = Addressable::URI.parse(page.current_url) + assert_equal("/admin/", uri.path) + assert(uri.fragment) + + fragment_uri = Addressable::URI.parse(uri.fragment) + assert_equal(fragment_path, fragment_uri.path) + if(fragment_query_values.nil?) + assert_nil(fragment_uri.query_values) + else + assert_equal(fragment_query_values, fragment_uri.query_values) + end + end + private @@test_rails_secret_token = nil diff --git a/test/support/api_umbrella_test_helpers/date_range_picker.rb b/test/support/api_umbrella_test_helpers/date_range_picker.rb new file mode 100644 index 000000000..542618604 --- /dev/null +++ b/test/support/api_umbrella_test_helpers/date_range_picker.rb @@ -0,0 +1,161 @@ +module ApiUmbrellaTestHelpers + module DateRangePicker + def assert_date_range_picker(fragment_path) + FactoryGirl.create(:log_item, :request_at => Time.parse("2015-01-16T06:06:28.816Z").utc) + LogItem.gateway.refresh_index! + + admin_login + + # Set the browser's time to 2015-01-24T03:00:00Z. + # + # Since the analytics timezone for tests is set to America/Denver (in + # config/test.yml), this time helps ensure the app takes into account the + # analytics timezone for date calculations, since this corresponds to + # 2015-01-23T20:00:00-07:00 (so the ending dates should all be 2015-01-23). + # + # Our usage of Zonebie to set a random local timezone for every test also + # helps ensure all the client-side date logic is correct and we always use + # the analytics timezone for date calculations, rather than the browser's + # local time. + page.execute_script("timekeeper.travel(Date.UTC(2015, 0, 24, 3, 0))") + + # Defaults to last 30 days. + visit "/admin/##{fragment_path}" + assert_download_csv_link_date_range("2014-12-25", "2015-01-23") + assert_date_range_picker_date_range("Last 30 Days", "2014-12-25", "2015-01-23") + assert_current_admin_url(fragment_path, nil) + + # Direct link to last 7 days. + visit "/admin/##{fragment_path}?date_range=7d" + assert_download_csv_link_date_range("2015-01-17", "2015-01-23") + assert_date_range_picker_date_range("Last 7 Days", "2015-01-17", "2015-01-23") + assert_current_admin_url(fragment_path, { "date_range" => "7d" }) + + # Direct link to a custom date range. + visit "/admin/##{fragment_path}?start_at=2015-01-19&end_at=2015-01-22" + assert_download_csv_link_date_range("2015-01-19", "2015-01-22") + assert_date_range_picker_date_range("Custom Range", "2015-01-19", "2015-01-22") + assert_current_admin_url(fragment_path, { + "start_at" => "2015-01-19", + "end_at" => "2015-01-22", + }) + + # Direct link to a custom date range that corresponds with a predefined + # range (last 7 days). + visit "/admin/##{fragment_path}?start_at=2015-01-17&end_at=2015-01-23" + assert_download_csv_link_date_range("2015-01-17", "2015-01-23") + assert_date_range_picker_date_range("Last 7 Days", "2015-01-17", "2015-01-23") + assert_current_admin_url(fragment_path, { + "start_at" => "2015-01-17", + "end_at" => "2015-01-23", + }) + + # Change to today in UI. + change_date_picker("Today") + assert_download_csv_link_date_range("2015-01-23", "2015-01-23") + assert_date_range_picker_date_range("Today", "2015-01-23", "2015-01-23") + assert_current_admin_url(fragment_path, { "date_range" => "today" }) + + # Change to last 30 days in UI. + change_date_picker("Last 30 Days") + assert_download_csv_link_date_range("2014-12-25", "2015-01-23") + assert_date_range_picker_date_range("Last 30 Days", "2014-12-25", "2015-01-23") + assert_current_admin_url(fragment_path, nil) + + # Change to a custom range in UI. + change_date_picker("Custom Range", "2014-12-31", "2015-01-23") + assert_download_csv_link_date_range("2014-12-31", "2015-01-23") + assert_date_range_picker_date_range("Custom Range", "2014-12-31", "2015-01-23") + assert_current_admin_url(fragment_path, { + "start_at" => "2014-12-31", + "end_at" => "2015-01-23", + }) + + # Change to a custom range in UI that corresponds with a predefined range + # (last 7 days). + change_date_picker("Custom Range", "2015-01-17", "2015-01-23") + assert_download_csv_link_date_range("2015-01-17", "2015-01-23") + assert_date_range_picker_date_range("Last 7 Days", "2015-01-17", "2015-01-23") + assert_current_admin_url(fragment_path, { + "start_at" => "2015-01-17", + "end_at" => "2015-01-23", + }) + + # Change back to predefined range in UI. + change_date_picker("Last 7 Days") + assert_download_csv_link_date_range("2015-01-17", "2015-01-23") + assert_date_range_picker_date_range("Last 7 Days", "2015-01-17", "2015-01-23") + assert_current_admin_url(fragment_path, { + "date_range" => "7d", + }) + + # Change the browser's time during the current session. + assert_text("Filter Results") + visit "/admin/#/" + refute_text("Filter Results") + page.execute_script("timekeeper.travel(Date.UTC(2015, 0, 26, 3, 0))") + + # Check that a relative URL works with the updated data. + visit "/admin/##{fragment_path}?date_range=7d" + assert_download_csv_link_date_range("2015-01-19", "2015-01-25") + assert_date_range_picker_date_range("Last 7 Days", "2015-01-19", "2015-01-25") + assert_current_admin_url(fragment_path, { "date_range" => "7d" }) + + # Check that static dates remain the same. + visit "/admin/##{fragment_path}?start_at=2015-01-17&end_at=2015-01-23" + assert_download_csv_link_date_range("2015-01-17", "2015-01-23") + assert_date_range_picker_date_range("Custom Range", "2015-01-17", "2015-01-23") + assert_current_admin_url(fragment_path, { + "start_at" => "2015-01-17", + "end_at" => "2015-01-23", + }) + ensure + page.execute_script("timekeeper.reset()") + end + + def assert_download_csv_link_date_range(start_at, end_at) + assert_link("Download CSV", :href => /start_at=#{start_at}/) + link = find_link("Download CSV") + uri = Addressable::URI.parse(link[:href]) + assert_equal(start_at, uri.query_values["start_at"]) + assert_equal(end_at, uri.query_values["end_at"]) + assert_nil(uri.query_values["date_range"]) + end + + def assert_date_range_picker_date_range(range_label, start_at, end_at) + start_at = Date.parse(start_at) + end_at = Date.parse(end_at) + + assert_text("#{start_at.strftime("%b %e, %Y")} - #{end_at.strftime("%b %e, %Y")}") + find("#reportrange a").click + assert_selector(".daterangepicker") + within(".daterangepicker") do + assert_selector(".ranges li.active", :text => range_label) + if(range_label == "Custom Range") + assert_selector(".calendar", :visible => :visible) + assert_field("daterangepicker_start", :with => start_at.strftime("%m/%d/%Y")) + assert_field("daterangepicker_end", :with => end_at.strftime("%m/%d/%Y")) + else + assert_selector(".calendar", :visible => :hidden) + end + click_button("Cancel") + end + refute_selector(".daterangepicker") + end + + def change_date_picker(range_label, start_at = nil, end_at = nil) + find("#reportrange a").click + within(".daterangepicker") do + find("li", :text => range_label).click + if(range_label == "Custom Range") + start_at = Date.parse(start_at) + end_at = Date.parse(end_at) + + fill_in("daterangepicker_start", :with => start_at.strftime("%m/%d/%Y")) + fill_in("daterangepicker_end", :with => end_at.strftime("%m/%d/%Y")) + click_button("Apply") + end + end + end + end +end diff --git a/test/support/capybara.rb b/test/support/capybara.rb index b20b44dde..10ba0793d 100644 --- a/test/support/capybara.rb +++ b/test/support/capybara.rb @@ -43,6 +43,7 @@ def write(msg) :extensions => [ File.join(API_UMBRELLA_SRC_ROOT, "test/support/capybara/disable_animations.js"), File.join(API_UMBRELLA_SRC_ROOT, "test/support/capybara/disable_fixed_header.js"), + File.join(API_UMBRELLA_SRC_ROOT, "test/support/capybara/timekeeper.js"), ], }) end diff --git a/test/support/capybara/timekeeper.js b/test/support/capybara/timekeeper.js new file mode 100644 index 000000000..2f3a2a325 --- /dev/null +++ b/test/support/capybara/timekeeper.js @@ -0,0 +1,201 @@ +/** + * Time keeper - EEasy testing of time-dependent code. + * + * Veselin Todorov + * MIT License. + */ + +!(function(name, root, definition) { + definition = definition(); + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = definition; + } + exports[name] = definition; + } else { + root[name] = definition; + } +})('timekeeper', this, function() { + + /** + * Native Date constructor reference. + * + * @type {Object} + */ + var NativeDate = Date; + + /** + * `TimeKeeper`. + * + * @type {Object} + */ + var timekeeper = {}; + + /** + * Frozen date time container. + * + * @type {Object} + */ + var freeze = null; + + /** + * Fake date time container. + * + * @type {Number} + */ + var travel = null; + + /** + * Travel start container. + * + * @type {Number} + */ + var started = null; + + /** + * Return the elapsed time. + * + * @returns {Number} + */ + function time() { + return travel + (NativeDate.now() - started); + } + + /** + * `FakeDate` constructor. + */ + function FakeDate(Y, M, D, h, m, s, ms) { + var length = arguments.length; + + if (this instanceof NativeDate) { + + if (!length && freeze) return new NativeDate(freeze.getTime()); + if (!length && travel) return new NativeDate(time()); + + var date = length == 1 && String(Y) === Y ? // isString(Y) + // We explicitly pass it through parse: + new NativeDate(NativeDate.parse(Y)) : + // We have to manually make calls depending on argument + // length here + length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) : + length >= 6 ? new NativeDate(Y, M, D, h, m, s) : + length >= 5 ? new NativeDate(Y, M, D, h, m) : + length >= 4 ? new NativeDate(Y, M, D, h) : + length >= 3 ? new NativeDate(Y, M, D) : + length >= 2 ? new NativeDate(Y, M) : + length >= 1 ? new NativeDate(Y) : + new NativeDate(); + // Prevent mixups with unfixed Date object + date.constructor = NativeDate; + return date; + } + return NativeDate.apply(this, arguments); + } + + // Copy any custom methods a 3rd party library may have added + (function() { + for (var key in NativeDate) { + FakeDate[key] = NativeDate[key]; + } + }()); + + // Copy "native" methods explicitly; they may be non-enumerable + FakeDate.UTC = NativeDate.UTC; + FakeDate.parse = NativeDate.parse; + + // Setup inheritance + FakeDate.prototype = NativeDate.prototype; + FakeDate.prototype.constructor = NativeDate; + + + /** + * Replace the original now method. + * + * Check if the time is + * + * - frozen and if so return the frozen time + * - faked and if so return the elapsed time + * + * @returns {Number} + * @api public + */ + FakeDate.now = function() { + if (freeze) return freeze.getTime(); + if (travel) return time(); + return NativeDate.now(); + }; + + /** + * Set current Date Time and freeze it. + * + * @param {Object|String|Number} Date. + * @api public + */ + timekeeper.freeze = function(date) { + useFakeDate(); + + if (typeof date !== 'object') { + date = new NativeDate(date); + } + + freeze = date; + }; + + /** + * Set current DateTime. + * + * @param {Object|String|Number} Date. + * @api public + */ + timekeeper.travel = function(date) { + useFakeDate(); + + if (typeof date !== 'object') { + date = new NativeDate(date); + } + + travel = date.getTime(); + started = NativeDate.now(); + }; + + /** + * Reset the `timekeeper` behavior. + * + * @api public + */ + timekeeper.reset = function() { + useNativeDate(); + freeze = null; + started = null; + travel = null; + }; + + /** + * Reflection: Are we currently modifying the native Date object? + * + * @api public + */ + timekeeper.isKeepingTime = function() { + return Date === FakeDate; + }; + + /** + * Replace the `Date` with `FakeDate`. + */ + function useFakeDate() { + Date = FakeDate + } + + /** + * Restore the `Date` to `NativeDate`. + */ + function useNativeDate() { + Date = NativeDate + } + + + /** + * Expose `timekeeper` + */ + return timekeeper; +});