refactor(templates): normalize Templates (#16642)

* refactor(templates): normalize Templates

* refactor(templates): fix types, lint, and tests

* fix(ui): update schemas

* fix(ui): remove unnecessary processStrategy from templates

* fix(ui): lint

* fix(ui): update thunks and selectors, and remove errant comments
pull/16412/head
Timmy Luong 2020-01-23 22:21:06 -08:00 committed by GitHub
parent 21e7eb7841
commit 67829a2b7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 443 additions and 288 deletions

View File

@ -24,7 +24,7 @@ import {
import {setViews} from 'src/views/actions/creators'
import {selectValue} from 'src/variables/actions/creators'
import {getVariables, refreshVariableValues} from 'src/variables/actions/thunks'
import {setExportTemplate} from 'src/templates/actions'
import {setExportTemplate} from 'src/templates/actions/creators'
import {checkDashboardLimits} from 'src/cloud/actions/limits'
import {updateViewAndVariables} from 'src/views/actions/thunks'
import * as creators from 'src/dashboards/actions/creators'

View File

@ -7,7 +7,7 @@ import ExportOverlay from 'src/shared/components/ExportOverlay'
// Actions
import {convertToTemplate as convertToTemplateAction} from 'src/dashboards/actions/thunks'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions/thunks'
// Types
import {DocumentCreate} from '@influxdata/influx'
@ -62,8 +62,8 @@ class DashboardExportOverlay extends PureComponent<Props> {
}
const mstp = (state: AppState): StateProps => ({
dashboardTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
dashboardTemplate: state.resources.templates.exportTemplate.item,
status: state.resources.templates.exportTemplate.status,
})
const mdtp: DispatchProps = {

View File

@ -15,7 +15,7 @@ import {getPlugins} from 'src/dataLoaders/actions/telegrafEditor'
import {getScrapers} from 'src/scrapers/actions/thunks'
import {getTasks} from 'src/tasks/actions/thunks'
import {getTelegrafs} from 'src/telegrafs/actions/thunks'
import {getTemplates} from 'src/templates/actions'
import {getTemplates} from 'src/templates/actions/thunks'
import {getVariables} from 'src/variables/actions/thunks'
// Types

View File

@ -0,0 +1,9 @@
// Types
import {AppState, ResourceType} from 'src/types'
export const getAll = (state: AppState, resource: ResourceType) => {
const {resources} = state
const allIDs: string[] = resources[resource].allIDs
const byID = resources[resource].byID
return allIDs.map(id => byID[id])
}

View File

@ -8,14 +8,15 @@ export const getResourcesStatus = (
const statuses = resources.map(resource => {
switch (resource) {
// Normalized resource statuses
case ResourceType.Members:
case ResourceType.Authorizations:
case ResourceType.Buckets:
case ResourceType.Telegrafs:
case ResourceType.Tasks:
case ResourceType.Scrapers:
case ResourceType.Variables:
case ResourceType.Dashboards:
case ResourceType.Authorizations: {
case ResourceType.Members:
case ResourceType.Scrapers:
case ResourceType.Tasks:
case ResourceType.Telegrafs:
case ResourceType.Templates:
case ResourceType.Variables: {
return state.resources[resource].status
}

View File

@ -5,13 +5,13 @@ import {omit} from 'lodash'
// Types
import {
Cell,
ResourceType,
Telegraf,
Task,
Dashboard,
Label,
RemoteDataState,
ResourceType,
Task,
Telegraf,
Variable,
Dashboard,
View,
} from 'src/types'
import {CellsWithViewProperties} from 'src/client'
@ -176,6 +176,12 @@ export const telegraf = new schema.Entity(
export const arrayOfTelegrafs = [telegraf]
/* Templates */
// Defines the schema for the "templates" resource
export const template = new schema.Entity(ResourceType.Templates)
export const arrayOfTemplates = [template]
/* Scrapers */
// Defines the schema for the "scrapers" resource

View File

@ -14,7 +14,7 @@ import {Controlled as ReactCodeMirror} from 'react-codemirror2'
import CopyButton from 'src/shared/components/CopyButton'
// Actions
import {createTemplateFromResource} from 'src/templates/actions/'
import {createTemplateFromResource} from 'src/templates/actions/thunks'
// Utils
import {downloadTextFile} from 'src/shared/utils/download'

View File

@ -20,7 +20,7 @@ export const formatStatValue = (
value: number | string = 0,
{decimalPlaces, prefix, suffix}: FormatStatValueOptions = {}
): string => {
let localeFormattedValue // undefined, string, or number
let localeFormattedValue: undefined | string | number
if (isNumber(value)) {
let digits: number

View File

@ -75,6 +75,7 @@ export const rootReducer = combineReducers<ReducerState>({
scrapers: scrapersReducer,
tasks: tasksReducer,
telegrafs: telegrafsReducer,
templates: templatesReducer,
tokens: authsReducer,
variables: variablesReducer,
views: viewsReducer,
@ -84,7 +85,6 @@ export const rootReducer = combineReducers<ReducerState>({
telegrafEditor: editorReducer,
telegrafEditorActivePlugins: activePluginsReducer,
telegrafEditorPlugins: pluginsReducer,
templates: templatesReducer,
timeMachines: timeMachinesReducer,
userSettings: userSettingsReducer,
variableEditor: variableEditorReducer,

View File

@ -11,7 +11,7 @@ import {createTaskFromTemplate as createTaskFromTemplateAJAX} from 'src/template
import * as schemas from 'src/schemas'
// Actions
import {setExportTemplate} from 'src/templates/actions'
import {setExportTemplate} from 'src/templates/actions/creators'
import {notify, Action as NotifyAction} from 'src/shared/actions/notifications'
import {
addTask,

View File

@ -7,7 +7,7 @@ import ExportOverlay from 'src/shared/components/ExportOverlay'
// Actions
import {convertToTemplate as convertToTemplateAction} from 'src/tasks/actions/thunks'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions/thunks'
// Types
import {AppState} from 'src/types'
@ -62,8 +62,8 @@ class TaskExportOverlay extends PureComponent<Props> {
}
const mstp = (state: AppState): StateProps => ({
taskTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
taskTemplate: state.resources.templates.exportTemplate.item,
status: state.resources.templates.exportTemplate.status,
})
const mdtp: DispatchProps = {

View File

@ -17,7 +17,7 @@ import GetResources from 'src/resources/components/GetResources'
// Actions
import {createTaskFromTemplate as createTaskFromTemplateAction} from 'src/tasks/actions/thunks'
import {getTemplateByID} from 'src/templates/actions'
import {getTemplateByID} from 'src/templates/actions/thunks'
// Types
import {
@ -30,6 +30,9 @@ import {
ResourceType,
} from 'src/types'
// Selectors
import {getAll} from 'src/resources/selectors/getAll'
interface StateProps {
templates: TemplateSummary[]
templateStatus: RemoteDataState
@ -138,7 +141,13 @@ class TaskImportFromTemplateOverlay extends PureComponent<
}
}
const mstp = ({templates: {items, status}}: AppState): StateProps => {
const mstp = (state: AppState): StateProps => {
const {
resources: {
templates: {status},
},
} = state
const items = getAll(state, ResourceType.Templates)
const filteredTemplates = items.filter(
t => !t.meta.type || t.meta.type === TemplateType.Task
)

View File

@ -0,0 +1,75 @@
// Types
import {RemoteDataState, TemplateSummaryEntities} from 'src/types'
import {DocumentCreate} from '@influxdata/influx'
import {NormalizedSchema} from 'normalizr'
export const ADD_TEMPLATE_SUMMARY = 'ADD_TEMPLATE_SUMMARY'
export const GET_TEMPLATE_SUMMARIES_FOR_ORG = 'GET_TEMPLATE_SUMMARIES_FOR_ORG'
export const POPULATE_TEMPLATE_SUMMARIES = 'POPULATE_TEMPLATE_SUMMARIES'
export const REMOVE_TEMPLATE_SUMMARY = 'REMOVE_TEMPLATE_SUMMARY'
export const SET_EXPORT_TEMPLATE = 'SET_EXPORT_TEMPLATE'
export const SET_TEMPLATE_SUMMARY = 'SET_TEMPLATE_SUMMARY'
export const SET_TEMPLATES_STATUS = 'SET_TEMPLATES_STATUS'
export type Action =
| ReturnType<typeof addTemplateSummary>
| ReturnType<typeof populateTemplateSummaries>
| ReturnType<typeof removeTemplateSummary>
| ReturnType<typeof setExportTemplate>
| ReturnType<typeof setTemplatesStatus>
| ReturnType<typeof setTemplateSummary>
type TemplateSummarySchema<R extends string | string[]> = NormalizedSchema<
TemplateSummaryEntities,
R
>
// Action Creators
export const addTemplateSummary = (schema: TemplateSummarySchema<string>) =>
({
type: ADD_TEMPLATE_SUMMARY,
schema,
} as const)
export const populateTemplateSummaries = (
schema: TemplateSummarySchema<string[]>
) =>
({
type: POPULATE_TEMPLATE_SUMMARIES,
status: RemoteDataState.Done,
schema,
} as const)
export const setExportTemplate = (
status: RemoteDataState,
item?: DocumentCreate
) =>
({
type: SET_EXPORT_TEMPLATE,
status,
item,
} as const)
export const setTemplatesStatus = (status: RemoteDataState) =>
({
type: SET_TEMPLATES_STATUS,
status,
} as const)
export const removeTemplateSummary = (id: string) =>
({
type: REMOVE_TEMPLATE_SUMMARY,
id,
} as const)
export const setTemplateSummary = (
id: string,
status: RemoteDataState,
schema?: TemplateSummarySchema<string>
) =>
({
type: SET_TEMPLATE_SUMMARY,
id,
status,
schema,
} as const)

View File

@ -1,136 +1,84 @@
// Utils
import {templateToExport} from 'src/shared/utils/resourceToTemplate'
// Libraries
import {normalize} from 'normalizr'
// APIs
import {client} from 'src/utils/api'
import {createDashboardFromTemplate} from 'src/dashboards/actions/thunks'
import {createVariableFromTemplate} from 'src/variables/actions/thunks'
import {createTaskFromTemplate} from 'src/tasks/actions/thunks'
// Schemas
import * as schemas from 'src/schemas'
// Actions
import {notify, Action as NotifyAction} from 'src/shared/actions/notifications'
import {
addTemplateSummary,
populateTemplateSummaries,
setExportTemplate,
setTemplatesStatus,
removeTemplateSummary,
setTemplateSummary,
Action as TemplateAction,
} from 'src/templates/actions/creators'
// Constants
import * as copy from 'src/shared/copy/notifications'
import {staticTemplates} from 'src/templates/constants/defaultTemplates'
// Types
import {
TemplateSummary,
DocumentCreate,
ITaskTemplate,
TemplateType,
ITemplate,
} from '@influxdata/influx'
import {Dispatch} from 'react'
import {DocumentCreate, ITaskTemplate, TemplateType} from '@influxdata/influx'
import {
RemoteDataState,
GetState,
DashboardTemplate,
VariableTemplate,
Template,
TemplateSummary,
TemplateSummaryEntities,
Label,
} from 'src/types'
// Actions
import {notify} from 'src/shared/actions/notifications'
// Constants
import * as copy from 'src/shared/copy/notifications'
// API
import {client} from 'src/utils/api'
import {createDashboardFromTemplate} from 'src/dashboards/actions/thunks'
import {createVariableFromTemplate} from 'src/variables/actions/thunks'
import {createTaskFromTemplate} from 'src/tasks/actions/thunks'
// Selectors
// Utils
import {templateToExport} from 'src/shared/utils/resourceToTemplate'
import {getOrg} from 'src/organizations/selectors'
export enum ActionTypes {
GetTemplateSummariesForOrg = 'GET_TEMPLATE_SUMMARIES_FOR_ORG',
PopulateTemplateSummaries = 'POPULATE_TEMPLATE_SUMMARIES',
SetTemplatesStatus = 'SET_TEMPLATES_STATUS',
SetExportTemplate = 'SET_EXPORT_TEMPLATE',
RemoveTemplateSummary = 'REMOVE_TEMPLATE_SUMMARY',
AddTemplateSummary = 'ADD_TEMPLATE_SUMMARY',
SetTemplateSummary = 'SET_TEMPLATE_SUMMARY',
}
type Action = TemplateAction | NotifyAction
export type Actions =
| PopulateTemplateSummaries
| SetTemplatesStatus
| SetExportTemplate
| RemoveTemplateSummary
| AddTemplateSummary
| SetTemplateSummary
export interface AddTemplateSummary {
type: ActionTypes.AddTemplateSummary
payload: {item: TemplateSummary}
}
export const addTemplateSummary = (
item: TemplateSummary
): AddTemplateSummary => ({
type: ActionTypes.AddTemplateSummary,
payload: {item},
})
export interface PopulateTemplateSummaries {
type: ActionTypes.PopulateTemplateSummaries
payload: {items: TemplateSummary[]; status: RemoteDataState}
}
export const populateTemplateSummaries = (
items: TemplateSummary[]
): PopulateTemplateSummaries => ({
type: ActionTypes.PopulateTemplateSummaries,
payload: {items, status: RemoteDataState.Done},
})
export interface SetTemplatesStatus {
type: ActionTypes.SetTemplatesStatus
payload: {status: RemoteDataState}
}
export const setTemplatesStatus = (
status: RemoteDataState
): SetTemplatesStatus => ({
type: ActionTypes.SetTemplatesStatus,
payload: {status},
})
export interface SetExportTemplate {
type: ActionTypes.SetExportTemplate
payload: {status: RemoteDataState; item?: DocumentCreate}
}
export const setExportTemplate = (
status: RemoteDataState,
item?: DocumentCreate
): SetExportTemplate => ({
type: ActionTypes.SetExportTemplate,
payload: {status, item},
})
interface RemoveTemplateSummary {
type: ActionTypes.RemoveTemplateSummary
payload: {templateID: string}
}
const removeTemplateSummary = (templateID: string): RemoveTemplateSummary => ({
type: ActionTypes.RemoveTemplateSummary,
payload: {templateID},
})
export const getTemplateByID = async (id: string) => {
export const getTemplateByID = async (id: string): Promise<Template> => {
const template = (await client.templates.get(id)) as Template
return template
}
export const getTemplates = () => async (dispatch, getState: GetState) => {
export const getTemplates = () => async (
dispatch: Dispatch<Action>,
getState: GetState
): Promise<void> => {
const org = getOrg(getState())
dispatch(setTemplatesStatus(RemoteDataState.Loading))
const items = await client.templates.getAll(org.id)
dispatch(populateTemplateSummaries(items))
const templateSummaries = normalize<
TemplateSummary,
TemplateSummaryEntities,
string[]
>(items, schemas.arrayOfTemplates)
dispatch(populateTemplateSummaries(templateSummaries))
}
export const createTemplate = (template: DocumentCreate) => async (
dispatch,
dispatch: Dispatch<Action>,
getState: GetState
) => {
try {
const org = getOrg(getState())
await client.templates.create({...template, orgID: org.id})
const item = await client.templates.create({...template, orgID: org.id})
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(item, schemas.template)
dispatch(addTemplateSummary(templateSummary))
dispatch(notify(copy.importTemplateSucceeded()))
} catch (e) {
console.error(e)
@ -141,37 +89,30 @@ export const createTemplate = (template: DocumentCreate) => async (
export const createTemplateFromResource = (
resource: DocumentCreate,
resourceName: string
) => async (dispatch, getState: GetState) => {
) => async (dispatch: Dispatch<Action>, getState: GetState) => {
try {
const org = getOrg(getState())
await client.templates.create({...resource, orgID: org.id})
dispatch(notify(copy.resourceSavedAsTemplate(resourceName)))
} catch (e) {
console.error(e)
dispatch(copy.saveResourceAsTemplateFailed(resourceName, e))
dispatch(notify(copy.saveResourceAsTemplateFailed(resourceName, e)))
}
}
interface SetTemplateSummary {
type: ActionTypes.SetTemplateSummary
payload: {id: string; templateSummary: TemplateSummary}
}
export const setTemplateSummary = (
id: string,
templateSummary: TemplateSummary
): SetTemplateSummary => ({
type: ActionTypes.SetTemplateSummary,
payload: {id, templateSummary},
})
export const updateTemplate = (id: string, props: TemplateSummary) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
setTemplateSummary(id, RemoteDataState.Loading)
try {
const {meta} = await client.templates.update(id, props)
const item = await client.templates.update(id, props)
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(item, schemas.template)
dispatch(setTemplateSummary(id, {...props, meta}))
dispatch(setTemplateSummary(id, RemoteDataState.Done, templateSummary))
dispatch(notify(copy.updateTemplateSucceeded()))
} catch (e) {
console.error(e)
@ -180,7 +121,7 @@ export const updateTemplate = (id: string, props: TemplateSummary) => async (
}
export const convertToTemplate = (id: string) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
try {
dispatch(setExportTemplate(RemoteDataState.Loading))
@ -195,12 +136,12 @@ export const convertToTemplate = (id: string) => async (
}
}
export const clearExportTemplate = () => dispatch => {
export const clearExportTemplate = () => (dispatch: Dispatch<Action>) => {
dispatch(setExportTemplate(RemoteDataState.NotStarted, null))
}
export const deleteTemplate = (templateID: string) => async (
dispatch
dispatch: Dispatch<Action>
): Promise<void> => {
try {
await client.templates.delete(templateID)
@ -213,19 +154,19 @@ export const deleteTemplate = (templateID: string) => async (
}
export const cloneTemplate = (templateID: string) => async (
dispatch,
dispatch: Dispatch<Action>,
getState: GetState
): Promise<void> => {
try {
const org = getOrg(getState())
const createdTemplate = await client.templates.clone(templateID, org.id)
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(createdTemplate, schemas.template)
dispatch(
addTemplateSummary({
...createdTemplate,
labels: createdTemplate.labels || [],
})
)
dispatch(addTemplateSummary(templateSummary))
dispatch(notify(copy.cloneTemplateSuccess()))
} catch (e) {
console.error(e)
@ -276,12 +217,19 @@ export const createResourceFromTemplate = (templateID: string) => async (
export const addTemplateLabelsAsync = (
templateID: string,
labels: Label[]
) => async (dispatch): Promise<void> => {
) => async (dispatch: Dispatch<Action>): Promise<void> => {
try {
await client.templates.addLabels(templateID, labels.map(l => l.id))
const template = await client.templates.get(templateID)
const item = await client.templates.get(templateID)
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(item, schemas.template)
dispatch(setTemplateSummary(templateID, templateToSummary(template)))
dispatch(
setTemplateSummary(templateID, RemoteDataState.Done, templateSummary)
)
} catch (error) {
console.error(error)
dispatch(notify(copy.addTemplateLabelFailed()))
@ -291,20 +239,21 @@ export const addTemplateLabelsAsync = (
export const removeTemplateLabelsAsync = (
templateID: string,
labels: Label[]
) => async (dispatch): Promise<void> => {
) => async (dispatch: Dispatch<Action>): Promise<void> => {
try {
await client.templates.removeLabels(templateID, labels.map(l => l.id))
const template = await client.templates.get(templateID)
const item = await client.templates.get(templateID)
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(item, schemas.template)
dispatch(setTemplateSummary(templateID, templateToSummary(template)))
dispatch(
setTemplateSummary(templateID, RemoteDataState.Done, templateSummary)
)
} catch (error) {
console.error(error)
dispatch(notify(copy.removeTemplateLabelFailed()))
}
}
const templateToSummary = (template: ITemplate): TemplateSummary => ({
id: template.id,
meta: template.meta,
labels: template.labels,
})

View File

@ -15,7 +15,7 @@ import {
import {ResourceCard} from '@influxdata/clockface'
// Actions
import {createResourceFromStaticTemplate} from 'src/templates/actions'
import {createResourceFromStaticTemplate} from 'src/templates/actions/thunks'
// Selectors
import {viewableLabels} from 'src/labels/selectors'

View File

@ -24,16 +24,15 @@ import {
createResourceFromTemplate,
removeTemplateLabelsAsync,
addTemplateLabelsAsync,
} from 'src/templates/actions'
} from 'src/templates/actions/thunks'
// Selectors
import {viewableLabels} from 'src/labels/selectors'
import {getOrg} from 'src/organizations/selectors'
// Types
import {TemplateSummary} from '@influxdata/influx'
import {ComponentColor} from '@influxdata/clockface'
import {AppState, Organization, Label} from 'src/types'
import {AppState, Organization, Label, TemplateSummary} from 'src/types'
// Constants
import {DEFAULT_TEMPLATE_NAME} from 'src/templates/constants'

View File

@ -9,7 +9,7 @@ import ExportOverlay from 'src/shared/components/ExportOverlay'
import {
convertToTemplate as convertToTemplateAction,
clearExportTemplate as clearExportTemplateAction,
} from 'src/templates/actions'
} from 'src/templates/actions/thunks'
// Types
import {DocumentCreate} from '@influxdata/influx'
@ -63,8 +63,8 @@ class TemplateExportOverlay extends PureComponent<Props> {
}
const mstp = (state: AppState): StateProps => ({
exportTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
exportTemplate: state.resources.templates.exportTemplate.item,
status: state.resources.templates.exportTemplate.status,
})
const mdtp: DispatchProps = {

View File

@ -9,10 +9,7 @@ import ImportOverlay from 'src/shared/components/ImportOverlay'
import {invalidJSON} from 'src/shared/copy/notifications'
// Actions
import {
createTemplate as createTemplateAction,
getTemplates as getTemplatesAction,
} from 'src/templates/actions'
import {createTemplate as createTemplateAction} from 'src/templates/actions/thunks'
import {notify as notifyAction} from 'src/shared/actions/notifications'
// Types
@ -29,7 +26,6 @@ interface State {
interface DispatchProps {
createTemplate: typeof createTemplateAction
getTemplates: typeof getTemplatesAction
notify: typeof notifyAction
}
@ -70,7 +66,7 @@ class TemplateImportOverlay extends PureComponent<Props> {
this.setState(() => ({status}))
private handleImportTemplate = (importString: string) => {
const {createTemplate, getTemplates, notify} = this.props
const {createTemplate, notify} = this.props
let template
this.updateOverlayStatus(ComponentStatus.Default)
@ -82,9 +78,6 @@ class TemplateImportOverlay extends PureComponent<Props> {
return
}
createTemplate(template)
getTemplates()
this.onDismiss()
}
}
@ -102,7 +95,6 @@ const mstp = (state: AppState, props: Props): StateProps => {
const mdtp: DispatchProps = {
notify: notifyAction,
createTemplate: createTemplateAction,
getTemplates: getTemplatesAction,
}
export default connect<StateProps, DispatchProps, Props>(

View File

@ -11,7 +11,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
import {
convertToTemplate as convertToTemplateAction,
clearExportTemplate as clearExportTemplateAction,
} from 'src/templates/actions'
} from 'src/templates/actions/thunks'
// Types
import {DocumentCreate} from '@influxdata/influx'
@ -74,8 +74,8 @@ class TemplateExportOverlay extends PureComponent<Props> {
}
const mstp = (state: AppState): StateProps => ({
exportTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
exportTemplate: state.resources.templates.exportTemplate.item,
status: state.resources.templates.exportTemplate.status,
})
const mdtp: DispatchProps = {

View File

@ -9,7 +9,7 @@ import EmptyTemplatesList from 'src/templates/components/EmptyTemplatesList'
import TemplateCard from 'src/templates/components/TemplateCard'
// Types
import {TemplateSummary} from '@influxdata/influx'
import {TemplateSummary} from 'src/types'
import {SortTypes} from 'src/shared/utils/sort'
import {Sort} from 'src/clockface'

View File

@ -13,7 +13,7 @@ import GetResources from 'src/resources/components/GetResources'
import SettingsTabbedPageHeader from 'src/settings/components/SettingsTabbedPageHeader'
// Types
import {TemplateSummary, AppState, ResourceType} from 'src/types'
import {AppState, ResourceType, Template, TemplateSummary} from 'src/types'
import {SortTypes} from 'src/shared/utils/sort'
import {
Sort,
@ -28,14 +28,19 @@ import {
import {staticTemplates as statics} from 'src/templates/constants/defaultTemplates'
// Selectors
import {getAll} from 'src/resources/selectors/getAll'
type TemplateOrSummary = Template | TemplateSummary
interface StaticTemplate {
name: string
template: TemplateSummary
template: TemplateOrSummary
}
const staticTemplates: StaticTemplate[] = _.map(statics, (template, name) => ({
name,
template: template as TemplateSummary,
template: template as TemplateOrSummary,
}))
interface OwnProps {
@ -198,8 +203,8 @@ class TemplatesPage extends PureComponent<Props, State> {
this.setState({searchTerm})
}
}
const mstp = ({templates}: AppState): StateProps => ({
templates: templates.items,
const mstp = (state: AppState): StateProps => ({
templates: getAll(state, ResourceType.Templates),
})
export default connect<StateProps, {}, {}>(

View File

@ -17,7 +17,7 @@ import GetResources from 'src/resources/components/GetResources'
// Actions
import {createDashboardFromTemplate as createDashboardFromTemplateAction} from 'src/dashboards/actions/thunks'
import {getTemplateByID} from 'src/templates/actions'
import {getTemplateByID} from 'src/templates/actions/thunks'
// Constants
import {influxdbTemplateList} from 'src/templates/constants/defaultTemplates'
@ -34,6 +34,9 @@ import {
ResourceType,
} from 'src/types'
// Selectors
import {getAll} from 'src/resources/selectors/getAll'
interface StateProps {
templates: TemplateSummary[]
templateStatus: RemoteDataState
@ -186,7 +189,13 @@ class DashboardImportFromTemplateOverlay extends PureComponent<
}
}
const mstp = ({templates: {items, status}}: AppState): StateProps => {
const mstp = (state: AppState): StateProps => {
const {
resources: {
templates: {status},
},
} = state
const items = getAll(state, ResourceType.Templates)
const filteredTemplates = items.filter(
t => !t.meta.type || t.meta.type === TemplateType.Dashboard
)
@ -196,7 +205,7 @@ const mstp = ({templates: {items, status}}: AppState): StateProps => {
)
return {
templates: [...templates, ...(influxdbTemplateList as TemplateSummary[])],
templates: [...templates, ...(influxdbTemplateList as any)],
templateStatus: status,
}
}

View File

@ -1,36 +1,117 @@
import templatesReducer, {defaultState} from 'src/templates/reducers'
import {setTemplateSummary} from 'src/templates/actions'
// Libraries
import {normalize} from 'normalizr'
describe('templatesReducer', () => {
describe('setTemplateSummary', () => {
it('can update the name of a template', () => {
const initialState = defaultState()
const initialTemplate = {
id: 'abc',
// Schema
import * as schemas from 'src/schemas'
// Reducer
import {templatesReducer as reducer} from 'src/templates/reducers'
// Actions
import {
addTemplateSummary,
populateTemplateSummaries,
removeTemplateSummary,
setTemplateSummary,
} from 'src/templates/actions/creators'
// Types
import {
RemoteDataState,
TemplateSummaryEntities,
TemplateSummary,
} from 'src/types'
const status = RemoteDataState.Done
const templateSummary = {
links: {
self: '/api/v2/documents/templates/051ff6b3a8d23000',
},
id: '1',
meta: {
name: 'foo',
type: 'dashboard',
description: 'A template dashboard for something',
version: '1',
},
labels: [],
meta: {name: 'Belcalis', version: '1'},
status,
}
initialState.items.push(initialTemplate)
const actual = templatesReducer(
initialState,
setTemplateSummary(initialTemplate.id, {
...initialTemplate,
meta: {...initialTemplate.meta, name: 'Cardi B'},
const exportTemplate = {status, item: null}
const initialState = () => ({
status,
byID: {
['1']: templateSummary,
['2']: {...templateSummary, id: '2'},
},
allIDs: [templateSummary.id, '2'],
exportTemplate,
})
describe('templates reducer', () => {
it('can set the templatess', () => {
const schema = normalize<
TemplateSummary,
TemplateSummaryEntities,
string[]
>([templateSummary], schemas.arrayOfTemplates)
const byID = schema.entities.templates
const allIDs = schema.result
const actual = reducer(undefined, populateTemplateSummaries(schema))
expect(actual.byID).toEqual(byID)
expect(actual.allIDs).toEqual(allIDs)
})
it('can add a template', () => {
const id = '3'
const anotherTemplateSummary = {...templateSummary, id}
const schema = normalize<TemplateSummary, TemplateSummaryEntities, string>(
anotherTemplateSummary,
schemas.template
)
const expected = {
...defaultState(),
items: [
{
...initialTemplate,
meta: {...initialTemplate.meta, name: 'Cardi B'},
},
],
}
const state = initialState()
const actual = reducer(state, addTemplateSummary(schema))
expect(actual.allIDs.length).toEqual(Number(id))
})
it('can remove a template', () => {
const allIDs = [templateSummary.id]
const byID = {[templateSummary.id]: templateSummary}
const state = initialState()
const expected = {status, byID, allIDs, exportTemplate}
const actual = reducer(state, removeTemplateSummary(state.allIDs[1]))
expect(actual).toEqual(expected)
})
it('can set a template', () => {
const name = 'updated name'
const loadedTemplateSummary = {
...templateSummary,
meta: {...templateSummary.meta, name: 'updated name'},
}
const schema = normalize<TemplateSummary, TemplateSummaryEntities, string>(
loadedTemplateSummary,
schemas.template
)
const state = initialState()
const actual = reducer(
state,
setTemplateSummary(templateSummary.id, RemoteDataState.Done, schema)
)
expect(actual.byID[templateSummary.id].meta.name).toEqual(name)
})
})

View File

@ -1,17 +1,30 @@
import {produce} from 'immer'
import {Actions, ActionTypes} from 'src/templates/actions/'
import {TemplateSummary, DocumentCreate} from '@influxdata/influx'
import {RemoteDataState} from 'src/types'
export interface TemplatesState {
status: RemoteDataState
items: TemplateSummary[]
exportTemplate: {status: RemoteDataState; item: DocumentCreate}
}
import {
Action,
ADD_TEMPLATE_SUMMARY,
POPULATE_TEMPLATE_SUMMARIES,
REMOVE_TEMPLATE_SUMMARY,
SET_EXPORT_TEMPLATE,
SET_TEMPLATE_SUMMARY,
SET_TEMPLATES_STATUS,
} from 'src/templates/actions/creators'
import {
ResourceType,
RemoteDataState,
TemplateSummary,
TemplatesState,
} from 'src/types'
import {
addResource,
removeResource,
setResource,
setResourceAtID,
} from 'src/resources/reducers/helpers'
export const defaultState = (): TemplatesState => ({
status: RemoteDataState.NotStarted,
items: [],
byID: {},
allIDs: [],
exportTemplate: {
status: RemoteDataState.NotStarted,
item: null,
@ -20,43 +33,34 @@ export const defaultState = (): TemplatesState => ({
export const templatesReducer = (
state: TemplatesState = defaultState(),
action: Actions
action: Action
): TemplatesState =>
produce(state, draftState => {
switch (action.type) {
case ActionTypes.PopulateTemplateSummaries: {
const {status, items} = action.payload
draftState.status = status
if (items) {
draftState.items = items
} else {
draftState.items = null
}
case POPULATE_TEMPLATE_SUMMARIES: {
setResource<TemplateSummary>(draftState, action, ResourceType.Templates)
return
}
case ActionTypes.SetTemplatesStatus: {
const {status} = action.payload
case SET_TEMPLATES_STATUS: {
const {status} = action
draftState.status = status
return
}
case ActionTypes.SetTemplateSummary: {
const updated = draftState.items.map(t => {
if (t.id === action.payload.id) {
return action.payload.templateSummary
}
return t
})
draftState.items = updated
case SET_TEMPLATE_SUMMARY: {
setResourceAtID<TemplateSummary>(
draftState,
action,
ResourceType.Templates
)
return
}
case ActionTypes.SetExportTemplate: {
const {status, item} = action.payload
case SET_EXPORT_TEMPLATE: {
const {status, item} = action
draftState.exportTemplate.status = status
if (item) {
@ -67,21 +71,14 @@ export const templatesReducer = (
return
}
case ActionTypes.RemoveTemplateSummary: {
const {templateID} = action.payload
const {items} = draftState
draftState.items = items.filter(l => {
return l.id !== templateID
})
case REMOVE_TEMPLATE_SUMMARY: {
removeResource<TemplateSummary>(draftState, action)
return
}
case ActionTypes.AddTemplateSummary: {
const {item} = action.payload
const {items} = draftState
draftState.items = [...items, item]
case ADD_TEMPLATE_SUMMARY: {
addResource<TemplateSummary>(draftState, action, ResourceType.Templates)
return
}

View File

@ -1,15 +1,16 @@
import {
Cell,
Bucket,
Dashboard,
Authorization,
Organization,
Bucket,
Cell,
Dashboard,
Member,
Organization,
RemoteDataState,
Telegraf,
Scraper,
View,
TasksState,
Telegraf,
TemplatesState,
VariablesState,
} from 'src/types'
@ -63,6 +64,7 @@ export interface ResourceState {
[ResourceType.Scrapers]: NormalizedState<Scraper>
[ResourceType.Tasks]: TasksState
[ResourceType.Telegrafs]: TelegrafsState
[ResourceType.Templates]: TemplatesState
[ResourceType.Variables]: VariablesState
[ResourceType.Views]: NormalizedState<View>
}

View File

@ -9,6 +9,7 @@ import {
Scraper,
Task,
Telegraf,
TemplateSummary,
Variable,
View,
} from 'src/types'
@ -88,6 +89,14 @@ export interface TaskEntities {
}
}
// TemplateSummaryEntities defines the result of normalizr's normalization
// of the "templates resource"
export interface TemplateSummaryEntities {
templates: {
[uuid: string]: TemplateSummary
}
}
// VariableEntities defines the result of normalizr's normalization
// of the "variables" resource
export interface VariableEntities {

View File

@ -16,7 +16,6 @@ import {
TelegrafEditorActivePluginState,
TelegrafEditorState,
} from 'src/dataLoaders/reducers/telegrafEditor'
import {TemplatesState} from 'src/templates/reducers'
import {RangeState} from 'src/dashboards/reducers/ranges'
import {UserSettingsState} from 'src/userSettings/reducers'
import {OverlayState} from 'src/overlays/reducers/overlays'
@ -53,7 +52,6 @@ export interface AppState {
telegrafEditorActivePlugins: TelegrafEditorActivePluginState
plugins: PluginResourceState
telegrafEditor: TelegrafEditorState
templates: TemplatesState
timeMachines: TimeMachinesState
timeRange: TimeRange
userSettings: UserSettingsState

View File

@ -1,10 +1,28 @@
import {
ILabel,
DocumentListEntry,
Document,
DocumentCreate,
DocumentListEntry,
DocumentMeta,
ILabel,
} from '@influxdata/influx'
import {Dashboard, View, Cell, Label, Variable} from 'src/types'
import {
Cell,
Dashboard,
Label,
NormalizedState,
RemoteDataState,
Variable,
View,
} from 'src/types'
export interface TemplateSummary extends DocumentListEntry {
labels: ILabel[]
status: RemoteDataState
}
export interface TemplatesState extends NormalizedState<TemplateSummary> {
exportTemplate: {status: RemoteDataState; item: DocumentCreate}
}
export enum TemplateType {
Label = 'label',
@ -168,7 +186,3 @@ export interface VariableTemplate extends TemplateBase {
}
export type Template = TaskTemplate | DashboardTemplate | VariableTemplate
export interface TemplateSummary extends DocumentListEntry {
labels: ILabel[]
}

View File

@ -3,7 +3,7 @@ import {normalize} from 'normalizr'
// Actions
import {notify} from 'src/shared/actions/notifications'
import {setExportTemplate} from 'src/templates/actions'
import {setExportTemplate} from 'src/templates/actions/creators'
import {
setValues,
setVariables,

View File

@ -7,7 +7,7 @@ import ExportOverlay from 'src/shared/components/ExportOverlay'
// Actions
import {convertToTemplate as convertToTemplateAction} from 'src/variables/actions/thunks'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions'
import {clearExportTemplate as clearExportTemplateAction} from 'src/templates/actions/thunks'
// Types
import {AppState} from 'src/types'
@ -62,8 +62,8 @@ class VariableExportOverlay extends PureComponent<Props> {
}
const mstp = (state: AppState): StateProps => ({
variableTemplate: state.templates.exportTemplate.item,
status: state.templates.exportTemplate.status,
variableTemplate: state.resources.templates.exportTemplate.item,
status: state.resources.templates.exportTemplate.status,
})
const mdtp: DispatchProps = {