commit
2bb548d7ff
|
@ -5,20 +5,28 @@ import {notifyPresentationMode} from 'src/shared/copy/notifications'
|
|||
|
||||
import {Dispatch} from 'redux'
|
||||
|
||||
import * as AppActions from 'src/types/actions/app'
|
||||
import {
|
||||
ActionTypes,
|
||||
EnablePresentationModeAction,
|
||||
DisablePresentationModeAction,
|
||||
DelayEnablePresentationModeDispatcher,
|
||||
SetAutoRefreshActionCreator,
|
||||
SetAutoRefreshAction,
|
||||
TemplateControlBarVisibilityToggledAction,
|
||||
} from 'src/types/actions/app'
|
||||
|
||||
// ephemeral state action creators
|
||||
|
||||
export const enablePresentationMode = (): AppActions.EnablePresentationModeAction => ({
|
||||
type: 'ENABLE_PRESENTATION_MODE',
|
||||
export const enablePresentationMode = (): EnablePresentationModeAction => ({
|
||||
type: ActionTypes.EnablePresentationMode,
|
||||
})
|
||||
|
||||
export const disablePresentationMode = (): AppActions.DisablePresentationModeAction => ({
|
||||
type: 'DISABLE_PRESENTATION_MODE',
|
||||
export const disablePresentationMode = (): DisablePresentationModeAction => ({
|
||||
type: ActionTypes.DisablePresentationMode,
|
||||
})
|
||||
|
||||
export const delayEnablePresentationMode: AppActions.DelayEnablePresentationModeDispatcher = () => async (
|
||||
dispatch: Dispatch<AppActions.EnablePresentationModeAction>
|
||||
export const delayEnablePresentationMode: DelayEnablePresentationModeDispatcher = () => async (
|
||||
dispatch: Dispatch<EnablePresentationModeAction>
|
||||
): Promise<NodeJS.Timer> =>
|
||||
setTimeout(() => {
|
||||
dispatch(enablePresentationMode())
|
||||
|
@ -27,20 +35,15 @@ export const delayEnablePresentationMode: AppActions.DelayEnablePresentationMode
|
|||
|
||||
// persistent state action creators
|
||||
|
||||
export const setAutoRefresh: AppActions.SetAutoRefreshActionCreator = (
|
||||
export const setAutoRefresh: SetAutoRefreshActionCreator = (
|
||||
milliseconds: number
|
||||
): AppActions.SetAutoRefreshAction => ({
|
||||
type: 'SET_AUTOREFRESH',
|
||||
): SetAutoRefreshAction => ({
|
||||
type: ActionTypes.SetAutoRefresh,
|
||||
payload: {
|
||||
milliseconds,
|
||||
},
|
||||
})
|
||||
|
||||
export const templateControlBarVisibilityToggled = (): AppActions.TemplateControlBarVisibilityToggledAction => ({
|
||||
type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED',
|
||||
})
|
||||
|
||||
export const noop = (): AppActions.NoopAction => ({
|
||||
type: 'NOOP',
|
||||
payload: {},
|
||||
export const templateControlBarVisibilityToggled = (): TemplateControlBarVisibilityToggledAction => ({
|
||||
type: ActionTypes.TemplateControlBarVisibilityToggled,
|
||||
})
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
import {getMe as getMeAJAX, updateMe as updateMeAJAX} from 'shared/apis/auth'
|
||||
|
||||
import {getLinksAsync} from 'shared/actions/links'
|
||||
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {notifyUserSwitchedOrgs} from 'shared/copy/notifications'
|
||||
|
||||
export const authExpired = auth => ({
|
||||
type: 'AUTH_EXPIRED',
|
||||
payload: {
|
||||
auth,
|
||||
},
|
||||
})
|
||||
|
||||
export const authRequested = () => ({
|
||||
type: 'AUTH_REQUESTED',
|
||||
})
|
||||
|
||||
export const meGetRequested = () => ({
|
||||
type: 'ME_GET_REQUESTED',
|
||||
})
|
||||
|
||||
export const meGetCompleted = ({me, auth, logoutLink}) => ({
|
||||
type: 'ME_GET_COMPLETED',
|
||||
payload: {
|
||||
me,
|
||||
auth,
|
||||
logoutLink,
|
||||
},
|
||||
})
|
||||
|
||||
export const meGetFailed = () => ({
|
||||
type: 'ME_GET_FAILED',
|
||||
})
|
||||
|
||||
export const meChangeOrganizationRequested = () => ({
|
||||
type: 'ME_CHANGE_ORGANIZATION_REQUESTED',
|
||||
})
|
||||
|
||||
export const meChangeOrganizationCompleted = () => ({
|
||||
type: 'ME_CHANGE_ORGANIZATION_COMPLETED',
|
||||
})
|
||||
|
||||
export const meChangeOrganizationFailed = () => ({
|
||||
type: 'ME_CHANGE_ORGANIZATION_FAILED',
|
||||
})
|
||||
|
||||
// shouldResetMe protects against `me` being nullified in Redux temporarily,
|
||||
// which currently causes the app to show a loading spinner until me is
|
||||
// re-hydrated. if `getMeAsync` is only being used to refresh me after creating
|
||||
// an organization, this is undesirable behavior
|
||||
export const getMeAsync = ({shouldResetMe = false} = {}) => async dispatch => {
|
||||
if (shouldResetMe) {
|
||||
dispatch(authRequested())
|
||||
dispatch(meGetRequested())
|
||||
}
|
||||
try {
|
||||
// These non-me objects are added to every response by some AJAX trickery
|
||||
const {data: me, auth, logoutLink} = await getMeAJAX()
|
||||
// TODO: eventually, get the links for auth and logout out of here and into linksGetCompleted
|
||||
dispatch(
|
||||
meGetCompleted({
|
||||
me,
|
||||
auth,
|
||||
logoutLink,
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
dispatch(meGetFailed())
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
// meChangeOrganizationAsync is for switching the user's current organization.
|
||||
//
|
||||
// Global links state also needs to be refreshed upon organization change so
|
||||
// that Admin Chronograf / Current Org User tab's link is valid, but this is
|
||||
// happening automatically because we are using a browser redirect to reload
|
||||
// the application. If at some point we stop using a redirect and instead
|
||||
// make it a seamless SPA experience, a la issue #2463, we'll need to make sure
|
||||
// links are still refreshed.
|
||||
export const meChangeOrganizationAsync = (
|
||||
url,
|
||||
organization
|
||||
) => async dispatch => {
|
||||
dispatch(meChangeOrganizationRequested())
|
||||
try {
|
||||
const {data: me, auth, logoutLink} = await updateMeAJAX(url, organization)
|
||||
const currentRole = me.roles.find(
|
||||
r => r.organization === me.currentOrganization.id
|
||||
)
|
||||
dispatch(
|
||||
notify(
|
||||
notifyUserSwitchedOrgs(me.currentOrganization.name, currentRole.name)
|
||||
)
|
||||
)
|
||||
dispatch(meChangeOrganizationCompleted())
|
||||
dispatch(meGetCompleted({me, auth, logoutLink}))
|
||||
|
||||
// refresh links after every successful meChangeOrganization to refresh
|
||||
// /organizations/:id/users link for Admin / Current Org Users page to load
|
||||
dispatch(getLinksAsync())
|
||||
// TODO: reload sources upon me change org if non-refresh behavior preferred
|
||||
// instead of current behavior on both invocations of meChangeOrganization,
|
||||
// which is to refresh index via router.push('')
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error))
|
||||
dispatch(meChangeOrganizationFailed())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
import {Dispatch} from 'redux'
|
||||
|
||||
import {
|
||||
getMe as getMeAJAX,
|
||||
updateMe as updateMeAJAX,
|
||||
} from 'src/shared/apis/auth'
|
||||
|
||||
import {getLinksAsync} from 'src/shared/actions/links'
|
||||
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {errorThrown} from 'src/shared/actions/errors'
|
||||
|
||||
import {notifyUserSwitchedOrgs} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Me, Organization} from 'src/types/auth'
|
||||
|
||||
export type Action =
|
||||
| AuthExpiredAction
|
||||
| AuthRequestedAction
|
||||
| MeGetRequestedAction
|
||||
| MeGetCompletedAction
|
||||
| MeGetRequestedAction
|
||||
| MeGetFailedAction
|
||||
| MeChangeOrganizationCompletedAction
|
||||
| MeChangeOrganizationFailedAction
|
||||
| MeChangeOrganizationRequestedAction
|
||||
|
||||
export enum ActionTypes {
|
||||
AuthExpired = 'AUTH_EXPIRED',
|
||||
AuthRequested = 'AUTH_REQUESTED',
|
||||
MeGetRequested = 'ME_GET_REQUESTED',
|
||||
MeGetCompleted = 'ME_GET_COMPLETED',
|
||||
MeGetFailed = 'ME_GET_FAILED',
|
||||
MeChangeOrganizationRequested = 'ME_CHANGE_ORGANIZATION_REQUESTED',
|
||||
MeChangeOrganizationCompleted = 'ME_CHANGE_ORGANIZATION_COMPLETED',
|
||||
MeChangeOrganizationFailed = 'ME_CHANGE_ORGANIZATION_FAILED',
|
||||
}
|
||||
|
||||
export interface AuthExpiredAction {
|
||||
type: ActionTypes.AuthExpired
|
||||
payload: {auth}
|
||||
}
|
||||
export const authExpired = (auth): AuthExpiredAction => ({
|
||||
type: ActionTypes.AuthExpired,
|
||||
payload: {
|
||||
auth,
|
||||
},
|
||||
})
|
||||
|
||||
export interface AuthRequestedAction {
|
||||
type: ActionTypes.AuthRequested
|
||||
}
|
||||
export const authRequested = (): AuthRequestedAction => ({
|
||||
type: ActionTypes.AuthRequested,
|
||||
})
|
||||
|
||||
export interface MeGetRequestedAction {
|
||||
type: ActionTypes.MeGetRequested
|
||||
}
|
||||
export const meGetRequested = (): MeGetRequestedAction => ({
|
||||
type: ActionTypes.MeGetRequested,
|
||||
})
|
||||
|
||||
export interface MeGetCompletedAction {
|
||||
type: ActionTypes.MeGetCompleted
|
||||
payload: {
|
||||
me: Me
|
||||
auth
|
||||
logoutLink: string
|
||||
}
|
||||
}
|
||||
export const meGetCompleted = ({
|
||||
me,
|
||||
auth,
|
||||
logoutLink,
|
||||
}): MeGetCompletedAction => ({
|
||||
type: ActionTypes.MeGetCompleted,
|
||||
payload: {
|
||||
me,
|
||||
auth,
|
||||
logoutLink,
|
||||
},
|
||||
})
|
||||
|
||||
export interface MeGetFailedAction {
|
||||
type: ActionTypes.MeGetFailed
|
||||
}
|
||||
export const meGetFailed = (): MeGetFailedAction => ({
|
||||
type: ActionTypes.MeGetFailed,
|
||||
})
|
||||
|
||||
export interface MeChangeOrganizationRequestedAction {
|
||||
type: ActionTypes.MeChangeOrganizationRequested
|
||||
}
|
||||
export const meChangeOrganizationRequested = (): MeChangeOrganizationRequestedAction => ({
|
||||
type: ActionTypes.MeChangeOrganizationRequested,
|
||||
})
|
||||
|
||||
export interface MeChangeOrganizationCompletedAction {
|
||||
type: ActionTypes.MeChangeOrganizationCompleted
|
||||
}
|
||||
export const meChangeOrganizationCompleted = (): MeChangeOrganizationCompletedAction => ({
|
||||
type: ActionTypes.MeChangeOrganizationCompleted,
|
||||
})
|
||||
|
||||
export interface MeChangeOrganizationFailedAction {
|
||||
type: ActionTypes.MeChangeOrganizationFailed
|
||||
}
|
||||
export const meChangeOrganizationFailed = (): MeChangeOrganizationFailedAction => ({
|
||||
type: ActionTypes.MeChangeOrganizationFailed,
|
||||
})
|
||||
|
||||
// shouldResetMe protects against `me` being nullified in Redux temporarily,
|
||||
// which currently causes the app to show a loading spinner until me is
|
||||
// re-hydrated. if `getMeAsync` is only being used to refresh me after creating
|
||||
// an organization, this is undesirable behavior
|
||||
export const getMeAsync = ({shouldResetMe = false} = {}) => async (
|
||||
dispatch: Dispatch<Action>
|
||||
): Promise<void> => {
|
||||
if (shouldResetMe) {
|
||||
dispatch(authRequested())
|
||||
dispatch(meGetRequested())
|
||||
}
|
||||
try {
|
||||
// These non-me objects are added to every response by some AJAX trickery
|
||||
const {data: me, auth, logoutLink} = await getMeAJAX()
|
||||
// TODO: eventually, get the links for auth and logout out of here and into linksGetCompleted
|
||||
dispatch(
|
||||
meGetCompleted({
|
||||
me,
|
||||
auth,
|
||||
logoutLink,
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
dispatch(meGetFailed())
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
// meChangeOrganizationAsync is for switching the user's current organization.
|
||||
//
|
||||
// Global links state also needs to be refreshed upon organization change so
|
||||
// that Admin Chronograf / Current Org User tab's link is valid, but this is
|
||||
// happening automatically because we are using a browser redirect to reload
|
||||
// the application. If at some point we stop using a redirect and instead
|
||||
// make it a seamless SPA experience, a la issue #2463, we'll need to make sure
|
||||
// links are still refreshed.
|
||||
export const meChangeOrganizationAsync = (
|
||||
url: string,
|
||||
organization: Organization
|
||||
) => async (dispatch): Promise<void> => {
|
||||
dispatch(meChangeOrganizationRequested())
|
||||
try {
|
||||
const {data: me, auth, logoutLink} = await updateMeAJAX(url, organization)
|
||||
const currentRole = me.roles.find(
|
||||
r => r.organization === me.currentOrganization.id
|
||||
)
|
||||
dispatch(
|
||||
notify(
|
||||
notifyUserSwitchedOrgs(me.currentOrganization.name, currentRole.name)
|
||||
)
|
||||
)
|
||||
dispatch(meChangeOrganizationCompleted())
|
||||
dispatch(meGetCompleted({me, auth, logoutLink}))
|
||||
|
||||
// refresh links after every successful meChangeOrganization to refresh
|
||||
// /organizations/:id/users link for Admin / Current Org Users page to load
|
||||
dispatch(getLinksAsync())
|
||||
// TODO: reload sources upon me change org if non-refresh behavior preferred
|
||||
// instead of current behavior on both invocations of meChangeOrganization,
|
||||
// which is to refresh index via router.push('')
|
||||
} catch (error) {
|
||||
dispatch(errorThrown(error))
|
||||
dispatch(meChangeOrganizationFailed())
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import {getLinks as getLinksAJAX} from 'shared/apis/links'
|
||||
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {linksLink} from 'shared/constants'
|
||||
|
||||
const linksGetRequested = () => ({
|
||||
type: 'LINKS_GET_REQUESTED',
|
||||
})
|
||||
|
||||
export const linksGetCompleted = links => ({
|
||||
type: 'LINKS_GET_COMPLETED',
|
||||
payload: {links},
|
||||
})
|
||||
|
||||
const linksGetFailed = () => ({
|
||||
type: 'LINKS_GET_FAILED',
|
||||
})
|
||||
|
||||
export const getLinksAsync = () => async dispatch => {
|
||||
dispatch(linksGetRequested())
|
||||
try {
|
||||
const {data} = await getLinksAJAX()
|
||||
dispatch(linksGetCompleted(data))
|
||||
} catch (error) {
|
||||
const message = `Failed to retrieve api links from ${linksLink}`
|
||||
dispatch(errorThrown(error, message))
|
||||
dispatch(linksGetFailed())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import {Dispatch} from 'redux'
|
||||
|
||||
import {getLinks as getLinksAJAX} from 'src/shared/apis/links'
|
||||
|
||||
import {errorThrown} from 'src/shared/actions/errors'
|
||||
|
||||
import {linksLink} from 'src/shared/constants'
|
||||
|
||||
import {AuthLinks} from 'src/types/auth'
|
||||
|
||||
export enum ActionTypes {
|
||||
LinksGetRequested = 'LINKS_GET_REQUESTED',
|
||||
LinksGetCompleted = 'LINKS_GET_COMPLETED',
|
||||
LinksGetFailed = 'LINKS_GET_FAILED',
|
||||
}
|
||||
|
||||
export interface LinksGetRequestedAction {
|
||||
type: ActionTypes.LinksGetRequested
|
||||
}
|
||||
const linksGetRequested = (): LinksGetRequestedAction => ({
|
||||
type: ActionTypes.LinksGetRequested,
|
||||
})
|
||||
|
||||
export interface LinksGetCompletedAction {
|
||||
type: ActionTypes.LinksGetCompleted
|
||||
payload: {links: AuthLinks}
|
||||
}
|
||||
export const linksGetCompleted = (
|
||||
links: AuthLinks
|
||||
): LinksGetCompletedAction => ({
|
||||
type: ActionTypes.LinksGetCompleted,
|
||||
payload: {links},
|
||||
})
|
||||
|
||||
export interface LinksGetFailedAction {
|
||||
type: ActionTypes.LinksGetFailed
|
||||
}
|
||||
const linksGetFailed = (): LinksGetFailedAction => ({
|
||||
type: ActionTypes.LinksGetFailed,
|
||||
})
|
||||
|
||||
export const getLinksAsync = () => async (
|
||||
dispatch: Dispatch<
|
||||
LinksGetRequestedAction | LinksGetCompletedAction | LinksGetFailedAction
|
||||
>
|
||||
): Promise<void> => {
|
||||
dispatch(linksGetRequested())
|
||||
try {
|
||||
const {data} = await getLinksAJAX()
|
||||
dispatch(linksGetCompleted(data))
|
||||
} catch (error) {
|
||||
const message = `Failed to retrieve api links from ${linksLink}`
|
||||
dispatch(errorThrown(error, message))
|
||||
dispatch(linksGetFailed())
|
||||
}
|
||||
}
|
|
@ -9,12 +9,15 @@ import {analyzeQueries} from 'src/shared/apis'
|
|||
import {DEFAULT_DURATION_MS} from 'src/shared/constants'
|
||||
import replaceTemplates, {replaceInterval} from 'src/tempVars/utils/replace'
|
||||
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||
import {noop} from 'src/shared/actions/app'
|
||||
|
||||
import {Source} from 'src/types'
|
||||
|
||||
import {Template} from 'src/types'
|
||||
|
||||
const noop = () => ({
|
||||
type: 'NOOP',
|
||||
payload: {},
|
||||
})
|
||||
interface Query {
|
||||
text: string
|
||||
database?: string
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
import {combineReducers} from 'redux'
|
||||
|
||||
import {AUTOREFRESH_DEFAULT} from 'shared/constants'
|
||||
import {AUTOREFRESH_DEFAULT} from 'src/shared/constants'
|
||||
import {ActionTypes, Action} from 'src/types/actions/app'
|
||||
|
||||
const initialState = {
|
||||
interface State {
|
||||
ephemeral: {
|
||||
inPresentationMode: boolean
|
||||
}
|
||||
persisted: {
|
||||
autoRefresh: number
|
||||
showTemplateControlBar: boolean
|
||||
}
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
ephemeral: {
|
||||
inPresentationMode: false,
|
||||
},
|
||||
|
@ -17,16 +28,19 @@ const {
|
|||
persisted: initialAppPersistedState,
|
||||
} = initialState
|
||||
|
||||
const appEphemeralReducer = (state = initialAppEphemeralState, action) => {
|
||||
const appEphemeralReducer = (
|
||||
state = initialAppEphemeralState,
|
||||
action: Action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case 'ENABLE_PRESENTATION_MODE': {
|
||||
case ActionTypes.EnablePresentationMode: {
|
||||
return {
|
||||
...state,
|
||||
inPresentationMode: true,
|
||||
}
|
||||
}
|
||||
|
||||
case 'DISABLE_PRESENTATION_MODE': {
|
||||
case ActionTypes.DisablePresentationMode: {
|
||||
return {
|
||||
...state,
|
||||
inPresentationMode: false,
|
||||
|
@ -38,16 +52,19 @@ const appEphemeralReducer = (state = initialAppEphemeralState, action) => {
|
|||
}
|
||||
}
|
||||
|
||||
const appPersistedReducer = (state = initialAppPersistedState, action) => {
|
||||
const appPersistedReducer = (
|
||||
state = initialAppPersistedState,
|
||||
action: Action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case 'SET_AUTOREFRESH': {
|
||||
case ActionTypes.SetAutoRefresh: {
|
||||
return {
|
||||
...state,
|
||||
autoRefresh: action.payload.milliseconds,
|
||||
}
|
||||
}
|
||||
|
||||
case 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED': {
|
||||
case ActionTypes.TemplateControlBarVisibilityToggled: {
|
||||
const {showTemplateControlBar} = state
|
||||
|
||||
return {...state, showTemplateControlBar: !showTemplateControlBar}
|
||||
|
@ -58,7 +75,7 @@ const appPersistedReducer = (state = initialAppPersistedState, action) => {
|
|||
}
|
||||
}
|
||||
|
||||
const appReducer = combineReducers({
|
||||
const appReducer = combineReducers<State>({
|
||||
ephemeral: appEphemeralReducer,
|
||||
persisted: appPersistedReducer,
|
||||
})
|
|
@ -1,72 +0,0 @@
|
|||
const getInitialState = () => ({
|
||||
links: null,
|
||||
me: null,
|
||||
isMeLoading: false,
|
||||
isAuthLoading: false,
|
||||
logoutLink: null,
|
||||
})
|
||||
|
||||
import {getMeRole} from 'shared/reducers/helpers/auth'
|
||||
|
||||
export const initialState = getInitialState()
|
||||
|
||||
const meGetCompleted = (state, {me}, isUsingAuth) => {
|
||||
let newMe = me
|
||||
|
||||
if (isUsingAuth) {
|
||||
newMe = {
|
||||
...newMe,
|
||||
role: getMeRole(me),
|
||||
currentOrganization: me.currentOrganization,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
me: {...newMe},
|
||||
isMeLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
const authReceived = (state, {auth: {links}}) => ({
|
||||
...state,
|
||||
links,
|
||||
isAuthLoading: false,
|
||||
})
|
||||
|
||||
const logoutLinkReceived = (state, {logoutLink}, isUsingAuth) => ({
|
||||
...state,
|
||||
logoutLink,
|
||||
isUsingAuth,
|
||||
})
|
||||
|
||||
const authReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case 'AUTH_EXPIRED': {
|
||||
const {
|
||||
auth: {links},
|
||||
} = action.payload
|
||||
return {...initialState, links}
|
||||
}
|
||||
case 'AUTH_REQUESTED': {
|
||||
return {...state, isAuthLoading: true}
|
||||
}
|
||||
case 'ME_GET_REQUESTED': {
|
||||
return {...state, isMeLoading: true}
|
||||
}
|
||||
case 'ME_GET_COMPLETED': {
|
||||
const {logoutLink} = action.payload
|
||||
const isUsingAuth = !!logoutLink
|
||||
|
||||
let newState = meGetCompleted(state, action.payload, isUsingAuth)
|
||||
newState = authReceived(newState, action.payload)
|
||||
newState = logoutLinkReceived(newState, action.payload, isUsingAuth)
|
||||
|
||||
return newState
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
export default authReducer
|
|
@ -0,0 +1,103 @@
|
|||
import {AuthMe, AuthLink} from 'src/types/auth'
|
||||
import {getMeRole} from 'src/shared/reducers/helpers/auth'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
import {ActionTypes} from 'src/shared/actions/auth'
|
||||
|
||||
interface State {
|
||||
links: AuthLink[] | null
|
||||
me: AuthMe | null
|
||||
isMeLoading: boolean
|
||||
isAuthLoading: boolean
|
||||
logoutLink: string | null
|
||||
isUsingAuth: boolean
|
||||
}
|
||||
|
||||
interface Auth {
|
||||
auth: {
|
||||
links: AuthLink[]
|
||||
}
|
||||
}
|
||||
|
||||
const getInitialState = (): State => ({
|
||||
links: null,
|
||||
me: null,
|
||||
isMeLoading: false,
|
||||
isAuthLoading: false,
|
||||
logoutLink: null,
|
||||
isUsingAuth: false,
|
||||
})
|
||||
|
||||
export const initialState = getInitialState()
|
||||
|
||||
const meGetCompleted = (
|
||||
state: State,
|
||||
{me}: {me: AuthMe},
|
||||
isUsingAuth: boolean
|
||||
): State => {
|
||||
let newMe = me
|
||||
|
||||
if (isUsingAuth) {
|
||||
newMe = {
|
||||
...newMe,
|
||||
role: getMeRole(me),
|
||||
currentOrganization: me.currentOrganization,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
me: {...newMe},
|
||||
isMeLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
const authReceived = (state: State, auth: Auth): State => {
|
||||
const links = getDeep<AuthLink[]>(auth, 'auth.links', [])
|
||||
return {
|
||||
...state,
|
||||
links,
|
||||
isAuthLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
const logoutLinkReceived = (
|
||||
state: State,
|
||||
{logoutLink}: {logoutLink: string},
|
||||
isUsingAuth: boolean
|
||||
): State => ({
|
||||
...state,
|
||||
logoutLink,
|
||||
isUsingAuth,
|
||||
})
|
||||
|
||||
const authReducer = (state: State = initialState, action): State => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.AuthExpired: {
|
||||
const {
|
||||
auth: {links},
|
||||
} = action.payload
|
||||
return {...initialState, links}
|
||||
}
|
||||
case ActionTypes.AuthRequested: {
|
||||
return {...state, isAuthLoading: true}
|
||||
}
|
||||
case ActionTypes.MeGetRequested: {
|
||||
return {...state, isMeLoading: true}
|
||||
}
|
||||
case ActionTypes.MeGetCompleted: {
|
||||
const {logoutLink} = action.payload
|
||||
const isUsingAuth = !!logoutLink
|
||||
|
||||
let newState = meGetCompleted(state, action.payload, isUsingAuth)
|
||||
newState = authReceived(newState, action.payload)
|
||||
newState = logoutLinkReceived(newState, action.payload, isUsingAuth)
|
||||
|
||||
return newState
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
export default authReducer
|
|
@ -1,8 +1,9 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
import {SUPERADMIN_ROLE, MEMBER_ROLE} from 'src/auth/Authorized'
|
||||
import {AuthMe} from 'src/types/auth'
|
||||
|
||||
export const getMeRole = me => {
|
||||
export const getMeRole = (me: AuthMe): string => {
|
||||
const currentRoleOrg = me.roles.find(
|
||||
role => me.currentOrganization.id === role.organization
|
||||
)
|
|
@ -1,13 +1,27 @@
|
|||
import {Dispatch} from 'redux'
|
||||
|
||||
export enum ActionTypes {
|
||||
EnablePresentationMode = 'ENABLE_PRESENTATION_MODE',
|
||||
DisablePresentationMode = 'DISABLE_PRESENTATION_MODE',
|
||||
SetAutoRefresh = 'SET_AUTOREFRESH',
|
||||
TemplateControlBarVisibilityToggled = 'TemplateControlBarVisibilityToggledAction',
|
||||
Noop = 'NOOP',
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| EnablePresentationModeAction
|
||||
| DisablePresentationModeAction
|
||||
| SetAutoRefreshAction
|
||||
| TemplateControlBarVisibilityToggledAction
|
||||
|
||||
export type EnablePresentationModeActionCreator = () => EnablePresentationModeAction
|
||||
|
||||
export interface EnablePresentationModeAction {
|
||||
type: 'ENABLE_PRESENTATION_MODE'
|
||||
type: ActionTypes.EnablePresentationMode
|
||||
}
|
||||
|
||||
export interface DisablePresentationModeAction {
|
||||
type: 'DISABLE_PRESENTATION_MODE'
|
||||
type: ActionTypes.DisablePresentationMode
|
||||
}
|
||||
|
||||
export type DelayEnablePresentationModeDispatcher = () => DelayEnablePresentationModeThunk
|
||||
|
@ -21,7 +35,7 @@ export type SetAutoRefreshActionCreator = (
|
|||
) => SetAutoRefreshAction
|
||||
|
||||
export interface SetAutoRefreshAction {
|
||||
type: 'SET_AUTOREFRESH'
|
||||
type: ActionTypes.SetAutoRefresh
|
||||
payload: {
|
||||
milliseconds: number
|
||||
}
|
||||
|
@ -30,10 +44,5 @@ export interface SetAutoRefreshAction {
|
|||
export type TemplateControlBarVisibilityToggledActionCreator = () => TemplateControlBarVisibilityToggledAction
|
||||
|
||||
export interface TemplateControlBarVisibilityToggledAction {
|
||||
type: 'TEMPLATE_CONTROL_BAR_VISIBILITY_TOGGLED'
|
||||
}
|
||||
|
||||
export interface NoopAction {
|
||||
type: 'NOOP'
|
||||
payload: object
|
||||
type: ActionTypes.TemplateControlBarVisibilityToggled
|
||||
}
|
||||
|
|
|
@ -8,8 +8,27 @@ export interface Organization {
|
|||
}
|
||||
|
||||
export interface Me {
|
||||
currentOrganization?: Organization
|
||||
role: Role
|
||||
currentOrganization?: Organization
|
||||
}
|
||||
|
||||
export interface AuthMe {
|
||||
id?: string
|
||||
role: string
|
||||
name: string
|
||||
provider: string
|
||||
scheme: string
|
||||
superAdmin: boolean
|
||||
logoutLink: string
|
||||
roles: Role[]
|
||||
links: {
|
||||
self: string
|
||||
}
|
||||
organizations?: Organization[]
|
||||
currentOrganization?: Organization
|
||||
isUsingAuth: boolean
|
||||
isMeLoading: boolean
|
||||
isAuthLoading: boolean
|
||||
}
|
||||
|
||||
export enum InfluxDBPermissions {
|
||||
|
@ -90,3 +109,11 @@ export interface AuthLinks {
|
|||
sources: string
|
||||
users: string
|
||||
}
|
||||
|
||||
export interface AuthLink {
|
||||
name: string
|
||||
label: string
|
||||
login: string
|
||||
logout: string
|
||||
callback: string
|
||||
}
|
||||
|
|
|
@ -2,22 +2,9 @@ import reducer from 'src/data_explorer/reducers/ui'
|
|||
|
||||
import {addQuery, deleteQuery} from 'src/data_explorer/actions/view'
|
||||
|
||||
const noopAction = () => {
|
||||
return {type: 'NOOP'}
|
||||
}
|
||||
|
||||
let state
|
||||
|
||||
describe('DataExplorer.Reducers.UI', () => {
|
||||
it('it sets the default state for UI', () => {
|
||||
const actual = reducer(state, noopAction())
|
||||
const expected = {
|
||||
queryIDs: [],
|
||||
}
|
||||
|
||||
expect(actual).toEqual(expected)
|
||||
})
|
||||
|
||||
it('it can add a query', () => {
|
||||
const actual = reducer(state, addQuery())
|
||||
expect(actual.queryIDs.length).toBe(1)
|
|
@ -1,10 +1,10 @@
|
|||
import appReducer from 'shared/reducers/app'
|
||||
import appReducer from 'src/shared/reducers/app'
|
||||
import {
|
||||
enablePresentationMode,
|
||||
disablePresentationMode,
|
||||
setAutoRefresh,
|
||||
templateControlBarVisibilityToggled,
|
||||
} from 'shared/actions/app'
|
||||
} from 'src/shared/actions/app'
|
||||
|
||||
describe('Shared.Reducers.appReducer', () => {
|
||||
const initialState = {
|
|
@ -1,11 +1,11 @@
|
|||
import {default as authReducer, initialState} from 'shared/reducers/auth'
|
||||
import {default as authReducer, initialState} from 'src/shared/reducers/auth'
|
||||
|
||||
import {
|
||||
authExpired,
|
||||
authRequested,
|
||||
meGetRequested,
|
||||
meGetCompleted,
|
||||
} from 'shared/actions/auth'
|
||||
} from 'src/shared/actions/auth'
|
||||
|
||||
const defaultAuth = {
|
||||
links: [
|
||||
|
@ -17,9 +17,16 @@ const defaultAuth = {
|
|||
callback: '/oauth/github/callback',
|
||||
},
|
||||
],
|
||||
me: null,
|
||||
isMeLoading: false,
|
||||
isAuthLoading: false,
|
||||
logoutLink: null,
|
||||
isUsingAuth: false,
|
||||
}
|
||||
|
||||
const defaultMe = {
|
||||
roles: [],
|
||||
currentOrganization: '',
|
||||
name: 'wishful_modal@overlay.technology',
|
||||
links: {
|
||||
self: '/chronograf/v1/users/wishful_modal@overlay.technology',
|
||||
|
@ -83,11 +90,13 @@ describe('Shared.Reducers.authReducer', () => {
|
|||
isAuthLoading: true,
|
||||
isMeLoading: true,
|
||||
}
|
||||
|
||||
const reducedState = authReducer(
|
||||
loadingState,
|
||||
meGetCompleted({
|
||||
me: defaultMe,
|
||||
auth: defaultAuth,
|
||||
logoutLink: '',
|
||||
})
|
||||
)
|
||||
|
|
@ -2,7 +2,6 @@ import _ from 'lodash'
|
|||
|
||||
import linksReducer from 'shared/reducers/links'
|
||||
import {linksGetCompleted} from 'shared/actions/links'
|
||||
import {noop} from 'shared/actions/app'
|
||||
|
||||
const links = {
|
||||
layouts: '/chronograf/v1/layouts',
|
||||
|
@ -25,8 +24,7 @@ const links = {
|
|||
|
||||
describe('Shared.Reducers.linksReducer', () => {
|
||||
it('can handle LINKS_GET_COMPLETED', () => {
|
||||
const initial = linksReducer(undefined, noop())
|
||||
const actual = linksReducer(initial, linksGetCompleted(links))
|
||||
const actual = linksReducer(undefined, linksGetCompleted(links))
|
||||
const expected = links
|
||||
expect(_.isEqual(actual, expected)).toBe(true)
|
||||
})
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
import buildInfluxQLQuery, {buildQuery} from 'utils/influxql'
|
||||
import buildInfluxQLQuery, {buildQuery} from 'src/utils/influxql'
|
||||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
|
||||
import {NONE, NULL_STRING} from 'shared/constants/queryFillOptions'
|
||||
import {NONE, NULL_STRING} from 'src/shared/constants/queryFillOptions'
|
||||
import {TYPE_QUERY_CONFIG} from 'src/dashboards/constants'
|
||||
|
||||
function mergeConfig(options) {
|
||||
return Object.assign({}, defaultQueryConfig(123), options)
|
||||
import {QueryConfig} from 'src/types'
|
||||
|
||||
function mergeConfig(options: Partial<QueryConfig>) {
|
||||
return {...defaultQueryConfig({id: '123'}), ...options}
|
||||
}
|
||||
|
||||
describe('buildInfluxQLQuery', () => {
|
||||
let config, timeBounds
|
||||
let config
|
||||
let timeBounds
|
||||
describe('when information is missing', () => {
|
||||
it('returns a null select statement', () => {
|
||||
expect(buildInfluxQLQuery({}, mergeConfig())).toBe(null)
|
||||
timeBounds = {lower: 'now() - 15m', upper: null}
|
||||
expect(buildInfluxQLQuery(timeBounds, mergeConfig({}))).toBe(null)
|
||||
|
||||
let merged = mergeConfig({database: 'db1'})
|
||||
let actual = buildInfluxQLQuery({}, merged)
|
||||
let actual = buildInfluxQLQuery(timeBounds, merged)
|
||||
expect(actual).toBe(null) // no measurement
|
||||
|
||||
merged = mergeConfig({database: 'db1', measurement: 'm1'})
|
||||
actual = buildInfluxQLQuery({}, merged)
|
||||
actual = buildInfluxQLQuery(timeBounds, merged)
|
||||
expect(actual).toBe(null) // no fields
|
||||
})
|
||||
})
|
||||
|
@ -31,10 +35,11 @@ describe('buildInfluxQLQuery', () => {
|
|||
measurement: 'm1',
|
||||
fields: [{value: 'f1', type: 'field'}],
|
||||
})
|
||||
timeBounds = {lower: null, upper: null}
|
||||
})
|
||||
|
||||
it('builds the right query', () => {
|
||||
expect(buildInfluxQLQuery({}, config)).toBe(
|
||||
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
|
||||
'SELECT "f1" FROM "db1".."m1"'
|
||||
)
|
||||
})
|
||||
|
@ -48,19 +53,21 @@ describe('buildInfluxQLQuery', () => {
|
|||
retentionPolicy: 'rp1',
|
||||
fields: [{value: 'f1', type: 'field'}],
|
||||
})
|
||||
timeBounds = {lower: 'now() - 1hr'}
|
||||
})
|
||||
|
||||
it('builds the right query', () => {
|
||||
expect(buildInfluxQLQuery({}, config)).toBe(
|
||||
'SELECT "f1" FROM "db1"."rp1"."m1"'
|
||||
)
|
||||
const actual = buildInfluxQLQuery(timeBounds, config)
|
||||
const expected = 'SELECT "f1" FROM "db1"."rp1"."m1"'
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('builds the right query with a time range', () => {
|
||||
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
|
||||
timeBounds = {lower: 'now() - 1hr', upper: null}
|
||||
const actual = buildInfluxQLQuery(timeBounds, config)
|
||||
const expected =
|
||||
'SELECT "f1" FROM "db1"."rp1"."m1" WHERE time > now() - 1hr'
|
||||
)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -72,10 +79,11 @@ describe('buildInfluxQLQuery', () => {
|
|||
retentionPolicy: 'rp1',
|
||||
fields: [{value: '*', type: 'field'}],
|
||||
})
|
||||
timeBounds = {lower: null, upper: null}
|
||||
})
|
||||
|
||||
it('does not quote the star', () => {
|
||||
expect(buildInfluxQLQuery({}, config)).toBe(
|
||||
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
|
||||
'SELECT * FROM "db1"."rp1"."m1"'
|
||||
)
|
||||
})
|
||||
|
@ -193,7 +201,8 @@ describe('buildInfluxQLQuery', () => {
|
|||
})
|
||||
|
||||
it('builds the right query', () => {
|
||||
expect(buildInfluxQLQuery({}, config)).toBe(
|
||||
timeBounds = {lower: null, upper: null}
|
||||
expect(buildInfluxQLQuery(timeBounds, config)).toBe(
|
||||
'SELECT "f0", "f1" FROM "db1"."rp1"."m0"'
|
||||
)
|
||||
})
|
Loading…
Reference in New Issue