Merge pull request #13186 from influxdata/reconcile-vars

Make variables org centric
pull/13223/head
Deniz Kusefoglu 2019-04-05 15:57:45 -07:00 committed by GitHub
commit d078e48565
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 167 additions and 581 deletions

View File

@ -11,7 +11,6 @@ import TabbedPageSection from 'src/shared/components/tabbed_page/TabbedPageSecti
import TabbedPage from 'src/shared/components/tabbed_page/TabbedPage'
import Settings from 'src/me/components/account/Settings'
import Telegrafs from 'src/configuration/components/Telegrafs'
import Variables from 'src/configuration/components/Variables'
import Scrapers from 'src/configuration/components/Scrapers'
import PageTitleWithOrg from 'src/shared/components/PageTitleWithOrg'
@ -72,15 +71,6 @@ class ConfigurationPage extends Component<Props> {
</GetResources>
</GetResources>
</TabbedPageSection>
<TabbedPageSection
id="variables_tab"
url="variables_tab"
title="Variables"
>
<GetResources resource={ResourceTypes.Variables}>
<Variables />
</GetResources>
</TabbedPageSection>
<TabbedPageSection
id="settings_tab"
url="settings_tab"

View File

@ -1,45 +0,0 @@
// Libraries
import React, {PureComponent} from 'react'
// Components
import {Overlay} from 'src/clockface'
import VariableForm from 'src/organizations/components/VariableForm'
// Types
import {IVariable as Variable, Organization} from '@influxdata/influx'
interface Props {
onCreateVariable: (variable: Variable) => void
onHideOverlay: () => void
orgs: Organization[]
visible: boolean
initialScript?: string
}
export default class CreateVariableOverlay extends PureComponent<Props> {
public render() {
const {
onHideOverlay,
onCreateVariable,
initialScript,
orgs,
visible,
} = this.props
return (
<Overlay visible={visible}>
<Overlay.Container maxWidth={1000}>
<Overlay.Heading title="Create Variable" onDismiss={onHideOverlay} />
<Overlay.Body>
<VariableForm
onCreateVariable={onCreateVariable}
onHideOverlay={onHideOverlay}
orgID={orgs[0].id} //TODO: display a list of orgs and have the user pick one
initialScript={initialScript}
/>
</Overlay.Body>
</Overlay.Container>
</Overlay>
)
}
}

View File

@ -1,205 +0,0 @@
// Libraries
import React, {PureComponent, ChangeEvent} from 'react'
import _ from 'lodash'
import {connect} from 'react-redux'
// Utils
import {
getVariables,
createVariable,
updateVariable,
deleteVariable,
} from 'src/variables/actions'
// Components
import {Input, EmptyState} from '@influxdata/clockface'
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
import CreateVariableOverlay from 'src/configuration/components/CreateVariableOverlay'
import VariableList from 'src/organizations/components/VariableList'
import FilterList from 'src/shared/components/Filter'
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
import GetLabels from 'src/configuration/components/GetLabels'
// Types
import {OverlayState} from 'src/types'
import {AppState} from 'src/types'
import {IVariable as Variable, Organization} from '@influxdata/influx'
import {IconFont, ComponentSize, RemoteDataState} from '@influxdata/clockface'
import {VariablesState} from 'src/variables/reducers'
interface StateProps {
variables: VariablesState
orgs: Organization[]
}
interface DispatchProps {
onCreateVariable: typeof createVariable
onUpdateVariable: typeof updateVariable
onDeleteVariable: typeof deleteVariable
}
type Props = StateProps & DispatchProps
interface State {
searchTerm: string
createOverlayState: OverlayState
importOverlayState: OverlayState
variables: Variable[]
}
class Variables extends PureComponent<Props, State> {
public state: State = {
searchTerm: '',
createOverlayState: OverlayState.Closed,
importOverlayState: OverlayState.Closed,
variables: this.variableList(this.props.variables),
}
public render() {
const {orgs, variables} = this.props
const {searchTerm, createOverlayState} = this.state
return (
<>
<TabbedPageHeader>
<Input
icon={IconFont.Search}
placeholder="Filter variables..."
widthPixels={290}
value={searchTerm}
onChange={this.handleFilterChange}
onBlur={this.handleFilterBlur}
/>
<AddResourceDropdown
resourceName="Variable"
onSelectImport={this.handleOpenImportOverlay}
onSelectNew={this.handleOpenCreateOverlay}
/>
</TabbedPageHeader>
<GetLabels>
<FilterList<Variable>
searchTerm={searchTerm}
searchKeys={['name']}
list={this.variableList(variables)}
sortByKey="name"
>
{v => (
<VariableList
variables={v}
emptyState={this.emptyState}
onDeleteVariable={this.handleDeleteVariable}
onUpdateVariable={this.handleUpdateVariable}
onFilterChange={this.handleFilterUpdate}
/>
)}
</FilterList>
</GetLabels>
<CreateVariableOverlay
onCreateVariable={this.handleCreateVariable}
onHideOverlay={this.handleCloseCreateOverlay}
orgs={orgs}
visible={createOverlayState === OverlayState.Open}
/>
</>
)
}
private get emptyState(): JSX.Element {
const {orgs} = this.props
const {searchTerm} = this.state
if (!searchTerm) {
return (
<EmptyState size={ComponentSize.Large}>
<EmptyState.Text
text={`${
orgs[0].name
} does not own any Variables , why not create one?`}
highlightWords={['Variables']}
/>
<AddResourceDropdown
resourceName="Variable"
onSelectImport={this.handleOpenImportOverlay}
onSelectNew={this.handleOpenCreateOverlay}
/>
</EmptyState>
)
}
return (
<EmptyState size={ComponentSize.Large}>
<EmptyState.Text text="No Variables match your query" />
</EmptyState>
)
}
private variableList(variables: VariablesState): Variable[] {
return Object.values(variables.variables)
.filter(d => d.status === RemoteDataState.Done)
.map(d => d.variable)
}
private handleFilterChange = (e: ChangeEvent<HTMLInputElement>) => {
this.handleFilterUpdate(e.target.value)
}
private handleFilterBlur(e: ChangeEvent<HTMLInputElement>) {
this.setState({searchTerm: e.target.value})
}
private handleFilterUpdate = (searchTerm: string) => {
this.setState({searchTerm})
}
private handleOpenImportOverlay = (): void => {}
private handleOpenCreateOverlay = (): void => {
this.setState({createOverlayState: OverlayState.Open})
}
private handleCloseCreateOverlay = (): void => {
this.setState({createOverlayState: OverlayState.Closed})
}
private handleCreateVariable = async (variable: Variable): Promise<void> => {
// TODO(chnn): Remove this handler in favor of connecting child components
// directly to Redux, and the same for `handleUpdateVariable` and
// `handleDeleteVariable`
const {onCreateVariable} = this.props
try {
await onCreateVariable(variable)
this.handleCloseCreateOverlay()
} catch (e) {}
}
private handleUpdateVariable = (variable: Partial<Variable>): void => {
const {onUpdateVariable} = this.props
onUpdateVariable(variable.id, variable)
}
private handleDeleteVariable = (variable: Variable): void => {
const {onDeleteVariable} = this.props
onDeleteVariable(variable.id)
}
}
const mstp = ({variables, orgs: {items}}: AppState): StateProps => {
return {
variables: variables,
orgs: items,
}
}
const mdtp = {
onGetVariables: getVariables,
onCreateVariable: createVariable,
onUpdateVariable: updateVariable,
onDeleteVariable: deleteVariable,
}
export default connect<StateProps, DispatchProps, {}>(
mstp,
mdtp
)(Variables)

View File

@ -37,7 +37,10 @@ import {setExportTemplate} from 'src/templates/actions'
// Utils
import {filterUnusedVars} from 'src/shared/utils/filterUnusedVars'
import {getVariablesForOrg, getHydratedVariables} from 'src/variables/selectors'
import {
extractVariablesList,
getHydratedVariables,
} from 'src/variables/selectors'
import {getViewsForDashboard} from 'src/dashboards/selectors'
import {
getNewDashboardCell,
@ -267,12 +270,10 @@ export const refreshDashboardVariableValues = (
dashboard: Dashboard,
nextViews: View[]
) => (dispatch, getState: GetState) => {
const variables = getVariablesForOrg(getState(), dashboard.orgID)
const variables = extractVariablesList(getState())
const variablesInUse = filterUnusedVars(variables, nextViews)
return dispatch(
refreshVariableValues(dashboard.id, dashboard.orgID, variablesInUse)
)
return dispatch(refreshVariableValues(dashboard.id, variablesInUse))
}
export const getDashboardAsync = (dashboardID: string) => async (
@ -472,23 +473,24 @@ export const selectVariableValue = (
dispatch(selectValue(dashboardID, variableID, value))
await dispatch(
refreshVariableValues(dashboard.id, dashboard.orgID, variables)
)
await dispatch(refreshVariableValues(dashboard.id, variables))
}
export const convertToTemplate = (dashboardID: string) => async (
dispatch
dispatch,
getState: GetState
): Promise<void> => {
try {
dispatch(setExportTemplate(RemoteDataState.Loading))
const {
orgs: {org},
} = getState()
const dashboard = await getDashboardAJAX(dashboardID)
const pendingViews = dashboard.cells.map(c =>
getViewAJAX(dashboardID, c.id)
)
const views = await Promise.all(pendingViews)
const allVariables = await client.variables.getAll()
const allVariables = await client.variables.getAll(org.id)
const variables = filterUnusedVars(allVariables, views)
const exportedVariables = exportVariables(variables, allVariables)
const dashboardTemplate = dashboardToTemplate(
@ -497,9 +499,7 @@ export const convertToTemplate = (dashboardID: string) => async (
exportedVariables
)
const orgID = dashboard.orgID // TODO remove when org is implicit app state
dispatch(setExportTemplate(RemoteDataState.Done, dashboardTemplate, orgID))
dispatch(setExportTemplate(RemoteDataState.Done, dashboardTemplate))
} catch (error) {
dispatch(setExportTemplate(RemoteDataState.Error))
dispatch(notify(copy.createTemplateFailed(error)))

View File

@ -15,7 +15,7 @@ import {AppState} from 'src/types'
import {RemoteDataState} from 'src/types'
interface OwnProps {
params: {dashboardID: string; orgID: string}
params: {dashboardID: string}
}
interface DispatchProps {
@ -26,7 +26,6 @@ interface DispatchProps {
interface StateProps {
dashboardTemplate: DocumentCreate
status: RemoteDataState
orgID: string
}
type Props = OwnProps & StateProps & DispatchProps & WithRouterProps
@ -49,18 +48,11 @@ class DashboardExportOverlay extends PureComponent<Props> {
resourceName="Dashboard"
resource={dashboardTemplate}
onDismissOverlay={this.onDismiss}
orgID={this.orgID}
status={status}
/>
)
}
private get orgID() {
const orgFromExistingResource = this.props.orgID
const orgInRoutes = this.props.params.orgID
return orgFromExistingResource || orgInRoutes
}
private onDismiss = () => {
const {router, clearExportTemplate} = this.props
@ -72,7 +64,6 @@ class DashboardExportOverlay extends PureComponent<Props> {
const mstp = (state: AppState): StateProps => ({
dashboardTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
orgID: state.templates.exportTemplate.orgID,
})
const mdtp: DispatchProps = {

View File

@ -4,14 +4,13 @@ import {withRouter, WithRouterProps} from 'react-router'
import {connect} from 'react-redux'
// Components
import VariableForm from 'src/organizations/components/VariableForm'
import VariableForm from 'src/variables/components/VariableForm'
// Utils
import {createVariable} from 'src/variables/actions'
// Types
import {AppState} from 'src/types'
import {IVariable as Variable} from '@influxdata/influx'
import {getActiveQuery} from 'src/timeMachine/selectors'
interface OwnProps {
@ -19,7 +18,7 @@ interface OwnProps {
}
interface DispatchProps {
onCreateVariable: (variable: Variable) => void
onCreateVariable: typeof createVariable
}
interface StateProps {
@ -30,16 +29,10 @@ type Props = StateProps & DispatchProps & OwnProps
class SaveAsVariable extends PureComponent<Props & WithRouterProps> {
render() {
const {
params: {orgID},
onHideOverlay,
onCreateVariable,
initialScript,
} = this.props
const {onHideOverlay, onCreateVariable, initialScript} = this.props
return (
<VariableForm
orgID={orgID}
onHideOverlay={onHideOverlay}
onCreateVariable={onCreateVariable}
initialScript={initialScript}

View File

@ -47,10 +47,10 @@ import OrgTelegrafsIndex from 'src/organizations/containers/OrgTelegrafsIndex'
import OrgTemplatesIndex from 'src/organizations/containers/OrgTemplatesIndex'
import TemplateImportOverlay from 'src/templates/components/TemplateImportOverlay'
import TemplateExportOverlay from 'src/templates/components/TemplateExportOverlay'
import OrgVariablesIndex from 'src/organizations/containers/OrgVariablesIndex'
import VariablesIndex from 'src/variables/containers/VariablesIndex'
import OrgScrapersIndex from 'src/organizations/containers/OrgScrapersIndex'
import VariableImportOverlay from 'src/variables/components/VariableImportOverlay'
import OrgVariableExportOverlay from 'src/organizations/components/OrgVariableExportOverlay'
import VariableExportOverlay from 'src/variables/components/VariableExportOverlay'
import SetOrg from 'src/shared/containers/SetOrg'
import RouteToOrg from 'src/shared/containers/RouteToOrg'
import CreateOrgOverlay from 'src/organizations/components/CreateOrgOverlay'
@ -199,14 +199,14 @@ class Root extends PureComponent {
component={TemplateExportOverlay}
/>
</Route>
<Route path="variables" component={OrgVariablesIndex}>
<Route path="variables" component={VariablesIndex}>
<Route
path="import"
component={VariableImportOverlay}
/>
<Route
path=":id/export"
component={OrgVariableExportOverlay}
component={VariableExportOverlay}
/>
</Route>
<Route path="scrapers" component={OrgScrapersIndex} />

View File

@ -1,7 +1,6 @@
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {get} from 'lodash'
import {client} from 'src/utils/api'
// Components
import {
@ -15,17 +14,10 @@ import {Controlled as ReactCodeMirror} from 'react-codemirror2'
import CopyButton from 'src/shared/components/CopyButton'
// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
// Constants
import {
resourceSavedAsTemplate,
saveResourceAsTemplateFailed,
} from 'src/shared/copy/notifications'
import {createTemplateFromResource} from 'src/templates/actions/'
// Utils
import {downloadTextFile} from 'src/shared/utils/download'
import {addOrgIDToTemplate} from 'src/shared/utils/resourceToTemplate'
// Types
import {DocumentCreate} from '@influxdata/influx'
@ -36,13 +28,12 @@ interface OwnProps {
onDismissOverlay: () => void
resource: DocumentCreate
resourceName: string
orgID: string
status: RemoteDataState
isVisible: boolean
}
interface DispatchProps {
notify: typeof notifyAction
createTemplateFromResource: typeof createTemplateFromResource
}
type Props = OwnProps & DispatchProps
@ -151,21 +142,14 @@ class ExportOverlay extends PureComponent<Props> {
}
private handleConvertToTemplate = async (): Promise<void> => {
const {resource, onDismissOverlay, orgID, notify, resourceName} = this.props
const template = addOrgIDToTemplate(resource, orgID)
try {
await client.templates.create(template)
notify(resourceSavedAsTemplate(resourceName))
} catch (error) {
notify(saveResourceAsTemplateFailed(resourceName, error))
}
const {resource, onDismissOverlay, resourceName} = this.props
createTemplateFromResource(resource, resourceName)
onDismissOverlay()
}
}
const mdtp: DispatchProps = {
notify: notifyAction,
createTemplateFromResource,
}
export default connect<{}, DispatchProps, OwnProps>(

View File

@ -59,8 +59,7 @@
@import 'src/organizations/components/Retention.scss';
@import 'src/organizations/components/TelegrafConfigOverlay.scss';
@import 'src/organizations/components/TelegrafExplainer.scss';
@import 'src/organizations/components/CreateVariableOverlay.scss';
@import 'src/organizations/components/CreateVariableOverlay.scss';
@import 'src/variables/components/CreateVariableOverlay.scss';
@import 'src/tasks/components/TaskForm.scss';
@import 'src/tasks/components/TasksPage.scss';
@import 'src/tasks/components/RunLogsList.scss';

View File

@ -504,19 +504,14 @@ export const getLogs = (taskID: string, runID: string) => async (
}
export const convertToTemplate = (taskID: string) => async (
dispatch,
getState: GetStateFunc
dispatch
): Promise<void> => {
try {
dispatch(setExportTemplate(RemoteDataState.Loading))
const {
orgs: {org},
} = await getState()
const task = await client.tasks.get(taskID)
const taskTemplate = taskToTemplate(task)
dispatch(setExportTemplate(RemoteDataState.Done, taskTemplate, org.id))
dispatch(setExportTemplate(RemoteDataState.Done, taskTemplate))
} catch (error) {
dispatch(setExportTemplate(RemoteDataState.Error))
dispatch(notify(copy.createTemplateFailed(error)))

View File

@ -15,7 +15,7 @@ import {DocumentCreate} from '@influxdata/influx'
import {RemoteDataState} from 'src/types'
interface OwnProps {
params: {id: string; orgID: string}
params: {id: string}
}
interface DispatchProps {
@ -26,7 +26,6 @@ interface DispatchProps {
interface StateProps {
taskTemplate: DocumentCreate
status: RemoteDataState
orgID: string
}
type Props = OwnProps & StateProps & DispatchProps & WithRouterProps
@ -49,18 +48,11 @@ class TaskExportOverlay extends PureComponent<Props> {
resourceName="Task"
resource={taskTemplate}
onDismissOverlay={this.onDismiss}
orgID={this.orgID}
status={status}
/>
)
}
private get orgID() {
const orgFromExistingResource = this.props.orgID
const orgInRoutes = this.props.params.orgID
return orgFromExistingResource || orgInRoutes
}
private onDismiss = () => {
const {router, clearExportTemplate} = this.props
@ -72,7 +64,6 @@ class TaskExportOverlay extends PureComponent<Props> {
const mstp = (state: AppState): StateProps => ({
taskTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
orgID: state.templates.exportTemplate.orgID,
})
const mdtp: DispatchProps = {

View File

@ -5,7 +5,7 @@ import {templateToExport} from 'src/shared/utils/resourceToTemplate'
// Types
import {TemplateSummary, DocumentCreate} from '@influxdata/influx'
import {RemoteDataState} from 'src/types'
import {RemoteDataState, GetState} from 'src/types'
// Actions
import {notify} from 'src/shared/actions/notifications'
@ -72,16 +72,15 @@ export const setTemplatesStatus = (
export interface SetExportTemplate {
type: ActionTypes.SetExportTemplate
payload: {status: RemoteDataState; item?: DocumentCreate; orgID: string}
payload: {status: RemoteDataState; item?: DocumentCreate}
}
export const setExportTemplate = (
status: RemoteDataState,
item?: DocumentCreate,
orgID?: string
item?: DocumentCreate
): SetExportTemplate => ({
type: ActionTypes.SetExportTemplate,
payload: {status, item, orgID},
payload: {status, item},
})
interface RemoveTemplateSummary {
@ -115,6 +114,22 @@ export const createTemplate = (template: DocumentCreate) => async dispatch => {
}
}
export const createTemplateFromResource = (
resource: DocumentCreate,
resourceName: string
) => async (dispatch, getState: GetState) => {
try {
const {
orgs: {org},
} = getState()
await client.templates.create({...resource, orgID: org.id})
dispatch(notify(copy.resourceSavedAsTemplate(resourceName)))
} catch (e) {
console.error(e)
dispatch(copy.saveResourceAsTemplateFailed(resourceName, e))
}
}
interface SetTemplateSummary {
type: ActionTypes.SetTemplateSummary
payload: {id: string; templateSummary: TemplateSummary}
@ -175,11 +190,16 @@ export const deleteTemplate = (templateID: string) => async (
}
}
export const cloneTemplate = (templateID: string, orgID: string) => async (
dispatch
export const cloneTemplate = (templateID: string) => async (
dispatch,
getState: GetState
): Promise<void> => {
try {
const createdTemplate = await client.templates.clone(templateID, orgID)
const {
orgs: {org},
} = getState()
const createdTemplate = await client.templates.clone(templateID, org.id)
dispatch(
addTemplateSummary({

View File

@ -185,7 +185,7 @@ const createVariablesFromTemplate = async (
}
const variablesIncluded = findIncludedVariables(included)
const existingVariables = await client.variables.getAll()
const existingVariables = await client.variables.getAll(orgID)
const variablesToCreate = findVariablesToCreate(
existingVariables,

View File

@ -115,10 +115,9 @@ class TemplateCard extends PureComponent<Props & WithRouterProps> {
private handleClone = () => {
const {
template: {id},
params: {orgID},
onClone,
} = this.props
onClone(id, orgID)
onClone(id)
}
private handleNameClick = (e: MouseEvent<HTMLAnchorElement>) => {

View File

@ -17,7 +17,7 @@ import {AppState} from 'src/types'
import {RemoteDataState} from 'src/types'
interface OwnProps {
params: {id: string; orgID: string}
params: {id: string}
}
interface DispatchProps {
@ -42,18 +42,13 @@ class TemplateExportOverlay extends PureComponent<Props> {
}
public render() {
const {
exportTemplate,
status,
params: {orgID},
} = this.props
const {exportTemplate, status} = this.props
return (
<ExportOverlay
resourceName="Template"
resource={exportTemplate}
onDismissOverlay={this.onDismiss}
orgID={orgID}
status={status}
/>
)

View File

@ -6,7 +6,7 @@ import {RemoteDataState} from 'src/types'
export interface TemplatesState {
status: RemoteDataState
items: TemplateSummary[]
exportTemplate: {status: RemoteDataState; item: DocumentCreate; orgID: string}
exportTemplate: {status: RemoteDataState; item: DocumentCreate}
}
export const defaultState = (): TemplatesState => ({
@ -15,7 +15,6 @@ export const defaultState = (): TemplatesState => ({
exportTemplate: {
status: RemoteDataState.NotStarted,
item: null,
orgID: null,
},
})
@ -53,7 +52,7 @@ export const templatesReducer = (
}
case ActionTypes.SetExportTemplate: {
const {status, item, orgID} = action.payload
const {status, item} = action.payload
draftState.exportTemplate.status = status
if (item) {
@ -61,12 +60,6 @@ export const templatesReducer = (
} else {
draftState.exportTemplate.item = null
}
if (orgID) {
draftState.exportTemplate.orgID = orgID
} else {
draftState.exportTemplate.orgID = null
}
return
}

View File

@ -14,7 +14,7 @@ import {getTimeRangeVars} from 'src/variables/utils/getTimeRangeVars'
import {filterUnusedVars} from 'src/shared/utils/filterUnusedVars'
import {checkQueryResult} from 'src/shared/utils/checkQueryResult'
import {
getVariablesForOrg,
extractVariablesList,
getVariable,
getHydratedVariables,
} from 'src/variables/selectors'
@ -63,8 +63,7 @@ export const refreshTimeMachineVariableValues = () => async (
...view,
properties: {...view.properties, queries: draftQueries},
}
const orgID = getState().orgs.org.id
const variables = getVariablesForOrg(getState(), orgID)
const variables = extractVariablesList(getState())
const variablesInUse = filterUnusedVars(variables, [view, draftView])
// Find variables whose values have already been loaded by the TimeMachine
@ -76,7 +75,7 @@ export const refreshTimeMachineVariableValues = () => async (
v => variablesInUse.includes(v) || hydratedVariables.includes(v)
)
await dispatch(refreshVariableValues(contextID, orgID, variablesToRefresh))
await dispatch(refreshVariableValues(contextID, variablesToRefresh))
}
let pendingResults: Array<WrappedCancelablePromise<ExecuteFluxQueryResult>> = []
@ -146,7 +145,6 @@ export const addVariableToTimeMachine = (variableID: string) => async (
getState: GetState
) => {
const contextID = getState().timeMachines.activeTimeMachineID
const orgID = getState().orgs.org.id
const variable = getVariable(getState(), variableID)
const variables = getHydratedVariables(getState(), contextID)
@ -155,7 +153,7 @@ export const addVariableToTimeMachine = (variableID: string) => async (
variables.push(variable)
}
await dispatch(refreshVariableValues(contextID, orgID, variables))
await dispatch(refreshVariableValues(contextID, variables))
}
export const selectVariableValue = (

View File

@ -1,48 +0,0 @@
// Libraries
import {PureComponent} from 'react'
import _ from 'lodash'
// APIs
import {client} from 'src/utils/api'
// Types
import {RemoteDataState} from 'src/types'
import {IVariable as Variable} from '@influxdata/influx'
interface Props {
children: (variables: Variable[], loading: RemoteDataState) => JSX.Element
}
interface State {
variables: Variable[]
loading: RemoteDataState
}
class FetchVariables extends PureComponent<Props, State> {
public state: State = {
variables: [],
loading: RemoteDataState.NotStarted,
}
public async componentDidMount() {
this.fetchVariables()
}
public render() {
const {variables, loading} = this.state
return this.props.children(variables, loading)
}
public fetchVariables = async () => {
this.setState({loading: RemoteDataState.Loading})
const variables = await client.variables.getAll()
this.setState({
variables: _.sortBy(variables, ['name']),
loading: RemoteDataState.Done,
})
}
}
export default FetchVariables

View File

@ -8,7 +8,7 @@ import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar
import VariableItem from 'src/timeMachine/components/variableToolbar/VariableItem'
// Utils
import {getVariablesForOrg} from 'src/variables/selectors'
import {extractVariablesList} from 'src/variables/selectors'
// Types
import {IVariable as Variable} from '@influxdata/influx'
@ -48,12 +48,10 @@ const VariableToolbar: FunctionComponent<OwnProps & StateProps> = ({
)
}
const mstp = (state: AppState) => {
const org = state.orgs.org
const variables = getVariablesForOrg(state, org.id)
const {status: variablesStatus} = state.variables
const mstp = (state: AppState): StateProps => {
const variables = extractVariablesList(state)
return {variables, variablesStatus}
return {variables}
}
export default connect<StateProps>(mstp)(VariableToolbar)

View File

@ -20,7 +20,7 @@ import {setExportTemplate} from 'src/templates/actions'
import {createVariableFromTemplate as createVariableFromTemplateAJAX} from 'src/templates/api'
// Utils
import {getValueSelections, getVariablesForOrg} from 'src/variables/selectors'
import {getValueSelections, extractVariablesList} from 'src/variables/selectors'
import {WrappedCancelablePromise, CancellationError} from 'src/types/promises'
import {variableToTemplate} from 'src/shared/utils/resourceToTemplate'
import {findDepedentVariables} from 'src/variables/utils/exportVariables'
@ -141,11 +141,16 @@ export const selectValue = (
payload: {contextID, variableID, selectedValue},
})
export const getVariables = () => async (dispatch: Dispatch<Action>) => {
export const getVariables = () => async (
dispatch: Dispatch<Action>,
getState: GetState
) => {
try {
dispatch(setVariables(RemoteDataState.Loading))
const variables = await client.variables.getAll()
const {
orgs: {org},
} = getState()
const variables = await client.variables.getAll(org.id)
dispatch(setVariables(RemoteDataState.Done, variables))
} catch (e) {
@ -171,11 +176,17 @@ export const getVariable = (id: string) => async (
}
}
export const createVariable = (variable: Variable) => async (
dispatch: Dispatch<Action>
) => {
export const createVariable = (
variable: Pick<Variable, 'name' | 'arguments'>
) => async (dispatch: Dispatch<Action>, getState: GetState) => {
try {
const createdVariable = await client.variables.create(variable)
const {
orgs: {org},
} = getState()
const createdVariable = await client.variables.create({
...variable,
orgID: org.id,
})
dispatch(
setVariable(createdVariable.id, RemoteDataState.Done, createdVariable)
@ -188,13 +199,15 @@ export const createVariable = (variable: Variable) => async (
}
export const createVariableFromTemplate = (
template: VariableTemplate,
orgID: string
) => async (dispatch: Dispatch<Action>) => {
template: VariableTemplate
) => async (dispatch: Dispatch<Action>, getState: GetState) => {
try {
const {
orgs: {org},
} = getState()
const createdVariable = await createVariableFromTemplateAJAX(
template,
orgID
org.id
)
dispatch(
@ -247,15 +260,17 @@ let pendingValueRequests: PendingValueRequests = {}
export const refreshVariableValues = (
contextID: string,
orgID: string,
variables: Variable[]
) => async (dispatch: Dispatch<Action>, getState: GetState): Promise<void> => {
dispatch(setValues(contextID, RemoteDataState.Loading))
try {
const {
orgs: {org},
} = getState()
const url = getState().links.query.self
const selections = getValueSelections(getState(), contextID)
const allVariables = getVariablesForOrg(getState(), orgID)
const allVariables = extractVariablesList(getState())
if (pendingValueRequests[contextID]) {
pendingValueRequests[contextID].cancel()
@ -263,7 +278,7 @@ export const refreshVariableValues = (
pendingValueRequests[contextID] = hydrateVars(variables, allVariables, {
url,
orgID,
orgID: org.id,
selections,
})
@ -281,19 +296,21 @@ export const refreshVariableValues = (
}
export const convertToTemplate = (variableID: string) => async (
dispatch
dispatch,
getState: GetState
): Promise<void> => {
try {
dispatch(setExportTemplate(RemoteDataState.Loading))
const {
orgs: {org},
} = getState()
const variable = await client.variables.get(variableID)
const allVariables = await client.variables.getAll()
const allVariables = await client.variables.getAll(org.id)
const dependencies = findDepedentVariables(variable, allVariables)
const variableTemplate = variableToTemplate(variable, dependencies)
const orgID = variable.orgID // TODO remove when org is implicit app state
dispatch(setExportTemplate(RemoteDataState.Done, variableTemplate, orgID))
dispatch(setExportTemplate(RemoteDataState.Done, variableTemplate))
} catch (error) {
dispatch(setExportTemplate(RemoteDataState.Error))
dispatch(notify(copy.createTemplateFailed(error)))

View File

@ -2,25 +2,24 @@
import React, {PureComponent} from 'react'
// Styles
import 'src/organizations/components/CreateVariableOverlay.scss'
import 'src/variables/components/CreateVariableOverlay.scss'
// Components
import {Overlay} from 'src/clockface'
import VariableForm from 'src/organizations/components/VariableForm'
import VariableForm from 'src/variables/components/VariableForm'
// Types
import {IVariable as Variable} from '@influxdata/influx'
interface Props {
onCreateVariable: (variable: Variable) => void
onCreateVariable: (variable: Pick<Variable, 'name' | 'arguments'>) => void
onHideOverlay: () => void
orgID: string
initialScript?: string
}
export default class CreateVariableOverlay extends PureComponent<Props> {
public render() {
const {onHideOverlay, onCreateVariable, orgID, initialScript} = this.props
const {onHideOverlay, onCreateVariable, initialScript} = this.props
return (
<Overlay.Container maxWidth={1000}>
@ -29,7 +28,6 @@ export default class CreateVariableOverlay extends PureComponent<Props> {
<VariableForm
onCreateVariable={onCreateVariable}
onHideOverlay={onHideOverlay}
orgID={orgID}
initialScript={initialScript}
/>
</Overlay.Body>

View File

@ -15,7 +15,7 @@ import {DocumentCreate} from '@influxdata/influx'
import {RemoteDataState} from 'src/types'
interface OwnProps {
params: {id: string; orgID: string}
params: {id: string}
}
interface DispatchProps {
@ -26,12 +26,11 @@ interface DispatchProps {
interface StateProps {
variableTemplate: DocumentCreate
status: RemoteDataState
orgID: string
}
type Props = OwnProps & StateProps & DispatchProps & WithRouterProps
class OrgVariableExportOverlay extends PureComponent<Props> {
class VariableExportOverlay extends PureComponent<Props> {
public async componentDidMount() {
const {
params: {id},
@ -49,18 +48,11 @@ class OrgVariableExportOverlay extends PureComponent<Props> {
resourceName="Variable"
resource={variableTemplate}
onDismissOverlay={this.onDismiss}
orgID={this.orgID}
status={status}
/>
)
}
private get orgID() {
const orgFromExistingResource = this.props.orgID
const orgInRoutes = this.props.params.orgID
return orgFromExistingResource || orgInRoutes
}
private onDismiss = () => {
const {router, clearExportTemplate} = this.props
@ -72,7 +64,6 @@ class OrgVariableExportOverlay extends PureComponent<Props> {
const mstp = (state: AppState): StateProps => ({
variableTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
orgID: state.templates.exportTemplate.orgID,
})
const mdtp: DispatchProps = {
@ -83,4 +74,4 @@ const mdtp: DispatchProps = {
export default connect<StateProps, DispatchProps, OwnProps>(
mstp,
mdtp
)(withRouter<Props>(OrgVariableExportOverlay))
)(withRouter<Props>(VariableExportOverlay))

View File

@ -15,9 +15,8 @@ import {
} from '@influxdata/clockface'
interface Props {
onCreateVariable: (variable: Variable) => void
onCreateVariable: (variable: Pick<Variable, 'name' | 'arguments'>) => void
onHideOverlay?: () => void
orgID: string
initialScript?: string
}
@ -28,7 +27,7 @@ interface State {
errorMessage: string
}
export default class CreateOrgOverlay extends PureComponent<Props, State> {
export default class VariableForm extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
@ -94,11 +93,10 @@ export default class CreateOrgOverlay extends PureComponent<Props, State> {
}
private handleSubmit = (): void => {
const {onCreateVariable, orgID, onHideOverlay} = this.props
const {onCreateVariable, onHideOverlay} = this.props
onCreateVariable({
name: this.state.name,
orgID,
arguments: {
type: 'query',
values: {query: this.state.script, language: 'flux'},

View File

@ -12,15 +12,11 @@ import {
} from 'src/variables/actions'
interface DispatchProps {
getVariables: typeof getVariablesAction
createVariableFromTemplate: typeof createVariableFromTemplateAction
getVariables: typeof getVariablesAction
}
interface OwnProps extends WithRouterProps {
params: {orgID: string}
}
type Props = DispatchProps & OwnProps
type Props = DispatchProps & WithRouterProps
class VariableImportOverlay extends PureComponent<Props> {
public render() {
@ -40,15 +36,13 @@ class VariableImportOverlay extends PureComponent<Props> {
}
private handleImportVariable = async (
uploadContent: string,
orgID: string
uploadContent: string
): Promise<void> => {
const {createVariableFromTemplate, getVariables} = this.props
const template = JSON.parse(uploadContent)
await createVariableFromTemplate(template, orgID)
await getVariables()
await createVariableFromTemplate(template)
getVariables()
this.onDismiss()
}

View File

@ -3,8 +3,8 @@ import React, {PureComponent} from 'react'
// Components
import {IndexList, Overlay} from 'src/clockface'
import VariableRow from 'src/organizations/components/VariableRow'
import UpdateVariableOverlay from 'src/organizations/components/UpdateVariableOverlay'
import VariableRow from 'src/variables/components/VariableRow'
import UpdateVariableOverlay from 'src/variables/components/UpdateVariableOverlay'
// Types
import {IVariable as Variable} from '@influxdata/influx'

View File

@ -3,7 +3,7 @@ import React from 'react'
import {shallow} from 'enzyme'
// Components
import VariableList from 'src/organizations/components/VariableList'
import VariableList from 'src/variables/components/VariableList'
// Constants
import {variables} from 'mocks/dummyData'

View File

@ -5,47 +5,40 @@ import {connect} from 'react-redux'
import {withRouter, WithRouterProps} from 'react-router'
// Utils
import {getVariablesForOrg} from 'src/variables/selectors'
import {
getVariables,
createVariable,
updateVariable,
deleteVariable,
} from 'src/variables/actions'
import {extractVariablesList} from 'src/variables/selectors'
// Components
import {Input, EmptyState, TechnoSpinner} from '@influxdata/clockface'
import {Input, EmptyState} from '@influxdata/clockface'
import {Overlay} from 'src/clockface'
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
import CreateVariableOverlay from 'src/organizations/components/CreateVariableOverlay'
import VariableList from 'src/organizations/components/VariableList'
import CreateVariableOverlay from 'src/variables/components/CreateVariableOverlay'
import VariableList from 'src/variables/components/VariableList'
import FilterList from 'src/shared/components/Filter'
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
import GetLabels from 'src/configuration/components/GetLabels'
// Types
import {OverlayState, RemoteDataState} from 'src/types'
import {OverlayState} from 'src/types'
import {AppState} from 'src/types'
import {IVariable as Variable, Organization} from '@influxdata/influx'
import {IVariable as Variable} from '@influxdata/influx'
import {IconFont, ComponentSize} from '@influxdata/clockface'
interface StateProps {
variables: Variable[]
variablesStatus: RemoteDataState
}
interface DispatchProps {
onGetVariables: typeof getVariables
onCreateVariable: typeof createVariable
onUpdateVariable: typeof updateVariable
onDeleteVariable: typeof deleteVariable
}
interface OwnProps {
org: Organization
}
type Props = StateProps & DispatchProps & OwnProps & WithRouterProps
type Props = StateProps & DispatchProps & WithRouterProps
interface State {
searchTerm: string
@ -53,29 +46,17 @@ interface State {
importOverlayState: OverlayState
}
class Variables extends PureComponent<Props, State> {
class VariablesTab extends PureComponent<Props, State> {
public state: State = {
searchTerm: '',
createOverlayState: OverlayState.Closed,
importOverlayState: OverlayState.Closed,
}
public componentDidMount() {
const {variablesStatus, onGetVariables} = this.props
if (variablesStatus === RemoteDataState.NotStarted) {
onGetVariables()
}
}
public render() {
const {variables, variablesStatus, org} = this.props
const {variables, onCreateVariable} = this.props
const {searchTerm, createOverlayState} = this.state
if (variablesStatus !== RemoteDataState.Done) {
return <TechnoSpinner />
}
return (
<>
<TabbedPageHeader>
@ -113,9 +94,8 @@ class Variables extends PureComponent<Props, State> {
</GetLabels>
<Overlay visible={createOverlayState === OverlayState.Open}>
<CreateVariableOverlay
onCreateVariable={this.handleCreateVariable}
onCreateVariable={onCreateVariable}
onHideOverlay={this.handleCloseCreateOverlay}
orgID={org.id}
/>
</Overlay>
</>
@ -123,16 +103,13 @@ class Variables extends PureComponent<Props, State> {
}
private get emptyState(): JSX.Element {
const {org} = this.props
const {searchTerm} = this.state
if (!searchTerm) {
return (
<EmptyState size={ComponentSize.Large}>
<EmptyState.Text
text={`${
org.name
} does not own any Variables , why not create one?`}
text={`Looks like there aren't any Variables, why not create one?`}
highlightWords={['Variables']}
/>
<AddResourceDropdown
@ -164,9 +141,12 @@ class Variables extends PureComponent<Props, State> {
}
private handleOpenImportOverlay = (): void => {
const {router, org} = this.props
const {
router,
params: {orgID},
} = this.props
router.push(`/orgs/${org.id}/variables/import`)
router.push(`/orgs/${orgID}/variables/import`)
}
private handleOpenCreateOverlay = (): void => {
@ -177,15 +157,6 @@ class Variables extends PureComponent<Props, State> {
this.setState({createOverlayState: OverlayState.Closed})
}
private handleCreateVariable = (variable: Variable): void => {
// TODO(chnn): Remove this handler in favor of connecting child components
// directly to Redux, and the same for `handleUpdateVariable` and
// `handleDeleteVariable`
const {onCreateVariable} = this.props
onCreateVariable(variable)
}
private handleUpdateVariable = (variable: Partial<Variable>): void => {
const {onUpdateVariable} = this.props
@ -199,21 +170,19 @@ class Variables extends PureComponent<Props, State> {
}
}
const mstp = (state: AppState, ownProps: OwnProps): StateProps => {
const variables = getVariablesForOrg(state, ownProps.org.id)
const {status: variablesStatus} = state.variables
const mstp = (state: AppState): StateProps => {
const variables = extractVariablesList(state)
return {variables, variablesStatus}
return {variables}
}
const mdtp = {
onGetVariables: getVariables,
const mdtp: DispatchProps = {
onCreateVariable: createVariable,
onUpdateVariable: updateVariable,
onDeleteVariable: deleteVariable,
}
export default connect<StateProps, DispatchProps, OwnProps>(
export default connect<StateProps, DispatchProps, {}>(
mstp,
mdtp
)(withRouter<OwnProps>(Variables))
)(withRouter<{}>(VariablesTab))

View File

@ -1,6 +1,5 @@
// Libraries
import React, {Component} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
import {connect} from 'react-redux'
// Components
@ -10,34 +9,21 @@ import OrgHeader from 'src/organizations/containers/OrgHeader'
import {Tabs} from 'src/clockface'
import {Page} from 'src/pageLayout'
import TabbedPageSection from 'src/shared/components/tabbed_page/TabbedPageSection'
import Variables from 'src/organizations/components/Variables'
//Actions
import * as NotificationsActions from 'src/types/actions/notifications'
import * as notifyActions from 'src/shared/actions/notifications'
import VariablesTab from 'src/variables/components/VariablesTab'
import GetResources, {
ResourceTypes,
} from 'src/configuration/components/GetResources'
// Types
import {Organization} from '@influxdata/influx'
import {AppState} from 'src/types'
interface RouterProps {
params: {
orgID: string
}
}
interface DispatchProps {
notify: NotificationsActions.PublishNotificationActionCreator
}
interface StateProps {
org: Organization
}
type Props = WithRouterProps & RouterProps & DispatchProps & StateProps
@ErrorHandling
class OrgVariablesIndex extends Component<Props> {
class VariablesIndex extends Component<StateProps> {
public render() {
const {org, children} = this.props
@ -55,7 +41,9 @@ class OrgVariablesIndex extends Component<Props> {
url="variables"
title="Variables"
>
<Variables org={org} />
<GetResources resource={ResourceTypes.Variables}>
<VariablesTab />
</GetResources>
</TabbedPageSection>
</Tabs.TabContents>
</Tabs>
@ -68,21 +56,9 @@ class OrgVariablesIndex extends Component<Props> {
}
}
const mstp = (state: AppState, props: Props) => {
const {
orgs: {items},
} = state
const org = items.find(o => o.id === props.params.orgID)
return {
org,
}
}
const mstp = ({orgs: {org}}: AppState): StateProps => ({org})
const mdtp: DispatchProps = {
notify: notifyActions.notify,
}
export default connect<StateProps, DispatchProps, {}>(
export default connect<StateProps, {}, {}>(
mstp,
mdtp
)(withRouter<{}>(OrgVariablesIndex))
null
)(VariablesIndex)

View File

@ -19,21 +19,16 @@ import {IVariable as Variable} from '@influxdata/influx'
type VariablesState = AppState['variables']['variables']
type ValuesState = AppState['variables']['values']['contextID']
const getVariablesForOrgMemoized = memoizeOne(
(variablesState: VariablesState, orgID: string) => {
const extractVariablesListMemoized = memoizeOne(
(variablesState: VariablesState): Variable[] => {
return Object.values(variablesState)
.filter(
d => d.status === RemoteDataState.Done && d.variable.orgID === orgID
)
.filter(d => d.status === RemoteDataState.Done)
.map(d => d.variable)
}
)
export const getVariablesForOrg = (
state: AppState,
orgID: string
): Variable[] => {
return getVariablesForOrgMemoized(state.variables.variables, orgID)
export const extractVariablesList = (state: AppState): Variable[] => {
return extractVariablesListMemoized(state.variables.variables)
}
const getVariablesForDashboardMemoized = memoizeOne(