Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ export const LOAD_PROJECT_SUMMARY = 'LOAD_PROJECT_SUMMARY'
export const LOAD_PROJECT_SUMMARY_PENDING = 'LOAD_PROJECT_SUMMARY_PENDING'
export const LOAD_PROJECT_SUMMARY_SUCCESS = 'LOAD_PROJECT_SUMMARY_SUCCESS'
export const LOAD_PROJECT_SUMMARY_FAILURE = 'LOAD_PROJECT_SUMMARY_FAILURE'
export const REFRESH_LOOKER_SESSION = 'REFRESH_LOOKER_SESSION'
export const SET_LOOKER_SESSION_EXPIRED = 'SET_LOOKER_SESSION_EXPIRED'

// Product attachments
export const ADD_PRODUCT_ATTACHMENT = 'ADD_PRODUCT_ATTACHMENT'
Expand Down Expand Up @@ -962,4 +962,7 @@ export const PROJECT_REPORTS = {
TAAS_MEMBERS : 'taas_members',
}

export const REPORT_SESSION_LENGTH = 1800
/**
* Report session length in seconds
*/
export const REPORT_SESSION_LENGTH = 30 * 60 // 30 minutes
13 changes: 7 additions & 6 deletions src/projects/actions/projectReports.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
LOAD_PROJECT_SUMMARY,
REFRESH_LOOKER_SESSION,
SET_LOOKER_SESSION_EXPIRED,
} from '../../config/constants'
import {
getProjectSummary,
Expand Down Expand Up @@ -33,14 +33,15 @@ export function loadProjectReportsUrls(projectId, reportName) {
}

/**
* Redux action to refresh the looker session. It is aimed to just indicate that there is need
* of refreshing the token. It does not do any thing itself. It is upto the state listner to react.
* Redux action set the flag `lookerSessionExpired`
*
* @param {Boolean} isExpired true to indicate that looker session is expired
*/
export function refreshLookerSession() {
export function setLookerSessionExpired(isExpired) {
return (dispatch) => {
return dispatch({
type: REFRESH_LOOKER_SESSION,
payload: { lookerSessionExpired: true }
type: SET_LOOKER_SESSION_EXPIRED,
payload: { lookerSessionExpired: isExpired }
})
}
}
77 changes: 55 additions & 22 deletions src/projects/detail/containers/ProjectSummaryReportContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PT from 'prop-types'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import MediaQuery from 'react-responsive'
import Modal from 'react-modal'

import {
SCREEN_BREAKPOINT_MD,
Expand All @@ -17,7 +18,7 @@ import Sticky from '../../../components/Sticky'
import ProjectInfoContainer from './ProjectInfoContainer'
import PERMISSIONS from '../../../config/permissions'
import { checkPermission } from '../../../helpers/permissions'
import { loadProjectSummary, loadProjectReportsUrls, refreshLookerSession } from '../../actions/projectReports'
import { loadProjectReportsUrls, setLookerSessionExpired } from '../../actions/projectReports'
import spinnerWhileLoading from '../../../components/LoadingSpinner'

import './ProjectSummaryReportContainer.scss'
Expand All @@ -34,34 +35,50 @@ class ProjectSummaryReportContainer extends React.Component {

constructor(props) {
super(props)

this.timer = null
this.setLookerSessionTimer = this.setLookerSessionTimer.bind(this)
this.reloadProjectReport = this.reloadProjectReport.bind(this)
}

reloadProjectReport() {
this.props.loadProjectReportsUrls(_.get(this.props, 'project.id'), PROJECT_REPORTS.PROJECT_SUMMARY)
// don't have to set session expire timer here, it would be set of iframe load
}

componentWillMount() {
this.reloadProjectReport()
// don't have to set session expire timer here, it would be set of iframe load
}

componentWillUnmount() {
if (this.timer) {
clearTimeout(this.timer)
}
}

componentWillUpdate(nextProps) {
const nextReportProjectId = _.get(nextProps, 'reportsProjectId')
const nextProjectId = _.get(nextProps, 'project.id')
const lookerSessionExpired = !this.props.lookerSessionExpired && nextProps.lookerSessionExpired
if(lookerSessionExpired || (nextProjectId && nextReportProjectId !== nextProjectId)) {
nextProps.loadProjectReportsUrls(nextProjectId, PROJECT_REPORTS.PROJECT_SUMMARY)
this.setLookerSessionTimer()

if (nextProjectId && nextReportProjectId !== nextProjectId) {
this.props.loadProjectReportsUrls(nextProjectId, PROJECT_REPORTS.PROJECT_SUMMARY)
// don't have to set session expire timer here, it would be set of iframe load
}
}

setLookerSessionTimer() {
console.log('Setting Looker Session Timer')

if (this.timer) {
clearTimeout(this.timer)
}
let timeoutDuration = 60*1000
if (REPORT_SESSION_LENGTH > 2*60) {
timeoutDuration = REPORT_SESSION_LENGTH*1000 - 2*60*1000
}
// set timeout for raising alert to refresh the token 2 minutes before the session expire

// set timeout for raising alert to refresh the token when session expires
this.timer = setTimeout(() => {
console.log('Calling refresh looker session action')
this.props.refreshLookerSession()
}, (timeoutDuration))
console.log('Looker Session is expired by timer')
this.props.setLookerSessionExpired(true)
}, REPORT_SESSION_LENGTH * 1000)
}

render() {
Expand All @@ -78,6 +95,7 @@ class ProjectSummaryReportContainer extends React.Component {
isLoading,
location,
projectSummaryEmbedUrl,
lookerSessionExpired,
} = this.props

const leftArea = (
Expand Down Expand Up @@ -111,13 +129,29 @@ class ProjectSummaryReportContainer extends React.Component {
</MediaQuery>
</TwoColsLayout.Sidebar>
<TwoColsLayout.Content>
{
<EnhancedLookerEmbedReport
isLoading={isLoading}
projectSummaryEmbedUrl={projectSummaryEmbedUrl}
onLoad={this.setLookerSessionTimer}
/>
}
<Modal
isOpen={lookerSessionExpired && !isLoading}
className="delete-post-dialog"
overlayClassName="delete-post-dialog-overlay"
contentLabel=""
>
<div className="modal-title">
Report sessions expired
</div>

<div className="modal-body">
To keep the data up to date, please, hit "Refresh" button to reload the report.
</div>

<div className="button-area flex center action-area">
<button className="tc-btn tc-btn-primary tc-btn-sm" onClick={this.reloadProjectReport}>Refresh</button>
</div>
</Modal>
<EnhancedLookerEmbedReport
isLoading={isLoading}
projectSummaryEmbedUrl={projectSummaryEmbedUrl}
onLoad={this.setLookerSessionTimer}
/>
</TwoColsLayout.Content>
</TwoColsLayout>
)
Expand Down Expand Up @@ -154,9 +188,8 @@ const mapStateToProps = ({ projectState, projectTopics, phasesTopics, projectRep
}

const mapDispatchToProps = {
loadProjectSummary,
loadProjectReportsUrls,
refreshLookerSession,
setLookerSessionExpired,
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ProjectSummaryReportContainer))
24 changes: 18 additions & 6 deletions src/projects/reducers/projectReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
LOAD_PROJECT_SUMMARY_PENDING,
LOAD_PROJECT_SUMMARY_SUCCESS,
LOAD_PROJECT_SUMMARY_FAILURE,
REFRESH_LOOKER_SESSION,
SET_LOOKER_SESSION_EXPIRED,
} from '../../config/constants'

const initialState = {
Expand All @@ -14,6 +14,19 @@ const initialState = {
lookerSessionExpired: false,
}

/**
* Adds a `random` query param to the URL so browser could treat such a URL as different.
*
* @param {String} url URL string to augment
*
* @returns {String} URL with `random` query param
*/
function addRandomParamToUrl(url) {
const randomParam = `random=${Math.random().toString().slice(2)}`

return url + (url.indexOf('?') > -1 ? '&' : '?') + randomParam
}

export const projectReports = function (state=initialState, action) {
const payload = action.payload

Expand All @@ -22,15 +35,15 @@ export const projectReports = function (state=initialState, action) {
return Object.assign({}, state, {
isLoading: true,
error: false,
projectId: action.meta.projectId
projectId: action.meta.projectId,
})

case LOAD_PROJECT_SUMMARY_SUCCESS:
if(action.meta.projectId === state.projectId) {
return Object.assign({}, state, {
isLoading: false,
error: false,
projectSummaryEmbedUrl: payload,
projectSummaryEmbedUrl: addRandomParamToUrl(payload),
lookerSessionExpired: false,
// projectSummary: payload
})
Expand All @@ -41,14 +54,13 @@ export const projectReports = function (state=initialState, action) {
case LOAD_PROJECT_SUMMARY_FAILURE: {
return Object.assign({}, state, {
isLoading: false,
lookerSessionExpired: false,
error: payload
})
}

case REFRESH_LOOKER_SESSION: {
case SET_LOOKER_SESSION_EXPIRED: {
return Object.assign({}, state, {
lookerSessionExpired: true
lookerSessionExpired: payload
})
}

Expand Down