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 {setViews} from 'src/views/actions/creators'
import {selectValue} from 'src/variables/actions/creators' import {selectValue} from 'src/variables/actions/creators'
import {getVariables, refreshVariableValues} from 'src/variables/actions/thunks' 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 {checkDashboardLimits} from 'src/cloud/actions/limits'
import {updateViewAndVariables} from 'src/views/actions/thunks' import {updateViewAndVariables} from 'src/views/actions/thunks'
import * as creators from 'src/dashboards/actions/creators' import * as creators from 'src/dashboards/actions/creators'

View File

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

View File

@ -15,7 +15,7 @@ import {getPlugins} from 'src/dataLoaders/actions/telegrafEditor'
import {getScrapers} from 'src/scrapers/actions/thunks' import {getScrapers} from 'src/scrapers/actions/thunks'
import {getTasks} from 'src/tasks/actions/thunks' import {getTasks} from 'src/tasks/actions/thunks'
import {getTelegrafs} from 'src/telegrafs/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' import {getVariables} from 'src/variables/actions/thunks'
// Types // 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 => { const statuses = resources.map(resource => {
switch (resource) { switch (resource) {
// Normalized resource statuses // Normalized resource statuses
case ResourceType.Members: case ResourceType.Authorizations:
case ResourceType.Buckets: case ResourceType.Buckets:
case ResourceType.Telegrafs:
case ResourceType.Tasks:
case ResourceType.Scrapers:
case ResourceType.Variables:
case ResourceType.Dashboards: 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 return state.resources[resource].status
} }

View File

@ -5,13 +5,13 @@ import {omit} from 'lodash'
// Types // Types
import { import {
Cell, Cell,
ResourceType, Dashboard,
Telegraf,
Task,
Label, Label,
RemoteDataState, RemoteDataState,
ResourceType,
Task,
Telegraf,
Variable, Variable,
Dashboard,
View, View,
} from 'src/types' } from 'src/types'
import {CellsWithViewProperties} from 'src/client' import {CellsWithViewProperties} from 'src/client'
@ -176,6 +176,12 @@ export const telegraf = new schema.Entity(
export const arrayOfTelegrafs = [telegraf] 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 */ /* Scrapers */
// Defines the schema for the "scrapers" resource // 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' import CopyButton from 'src/shared/components/CopyButton'
// Actions // Actions
import {createTemplateFromResource} from 'src/templates/actions/' import {createTemplateFromResource} from 'src/templates/actions/thunks'
// Utils // Utils
import {downloadTextFile} from 'src/shared/utils/download' import {downloadTextFile} from 'src/shared/utils/download'

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import {createTaskFromTemplate as createTaskFromTemplateAJAX} from 'src/template
import * as schemas from 'src/schemas' import * as schemas from 'src/schemas'
// Actions // 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 {notify, Action as NotifyAction} from 'src/shared/actions/notifications'
import { import {
addTask, addTask,

View File

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

View File

@ -17,7 +17,7 @@ import GetResources from 'src/resources/components/GetResources'
// Actions // Actions
import {createTaskFromTemplate as createTaskFromTemplateAction} from 'src/tasks/actions/thunks' import {createTaskFromTemplate as createTaskFromTemplateAction} from 'src/tasks/actions/thunks'
import {getTemplateByID} from 'src/templates/actions' import {getTemplateByID} from 'src/templates/actions/thunks'
// Types // Types
import { import {
@ -30,6 +30,9 @@ import {
ResourceType, ResourceType,
} from 'src/types' } from 'src/types'
// Selectors
import {getAll} from 'src/resources/selectors/getAll'
interface StateProps { interface StateProps {
templates: TemplateSummary[] templates: TemplateSummary[]
templateStatus: RemoteDataState 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( const filteredTemplates = items.filter(
t => !t.meta.type || t.meta.type === TemplateType.Task 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 // Libraries
import {templateToExport} from 'src/shared/utils/resourceToTemplate' 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' import {staticTemplates} from 'src/templates/constants/defaultTemplates'
// Types // Types
import { import {Dispatch} from 'react'
TemplateSummary, import {DocumentCreate, ITaskTemplate, TemplateType} from '@influxdata/influx'
DocumentCreate,
ITaskTemplate,
TemplateType,
ITemplate,
} from '@influxdata/influx'
import { import {
RemoteDataState, RemoteDataState,
GetState, GetState,
DashboardTemplate, DashboardTemplate,
VariableTemplate, VariableTemplate,
Template, Template,
TemplateSummary,
TemplateSummaryEntities,
Label, Label,
} from 'src/types' } from 'src/types'
// Actions // Utils
import {notify} from 'src/shared/actions/notifications' import {templateToExport} from 'src/shared/utils/resourceToTemplate'
// 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
import {getOrg} from 'src/organizations/selectors' import {getOrg} from 'src/organizations/selectors'
export enum ActionTypes { type Action = TemplateAction | NotifyAction
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',
}
export type Actions = export const getTemplateByID = async (id: string): Promise<Template> => {
| 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) => {
const template = (await client.templates.get(id)) as Template const template = (await client.templates.get(id)) as Template
return 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()) const org = getOrg(getState())
dispatch(setTemplatesStatus(RemoteDataState.Loading)) dispatch(setTemplatesStatus(RemoteDataState.Loading))
const items = await client.templates.getAll(org.id) 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 ( export const createTemplate = (template: DocumentCreate) => async (
dispatch, dispatch: Dispatch<Action>,
getState: GetState getState: GetState
) => { ) => {
try { try {
const org = getOrg(getState()) 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())) dispatch(notify(copy.importTemplateSucceeded()))
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -141,37 +89,30 @@ export const createTemplate = (template: DocumentCreate) => async (
export const createTemplateFromResource = ( export const createTemplateFromResource = (
resource: DocumentCreate, resource: DocumentCreate,
resourceName: string resourceName: string
) => async (dispatch, getState: GetState) => { ) => async (dispatch: Dispatch<Action>, getState: GetState) => {
try { try {
const org = getOrg(getState()) const org = getOrg(getState())
await client.templates.create({...resource, orgID: org.id}) await client.templates.create({...resource, orgID: org.id})
dispatch(notify(copy.resourceSavedAsTemplate(resourceName))) dispatch(notify(copy.resourceSavedAsTemplate(resourceName)))
} catch (e) { } catch (e) {
console.error(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 ( export const updateTemplate = (id: string, props: TemplateSummary) => async (
dispatch dispatch: Dispatch<Action>
): Promise<void> => { ): Promise<void> => {
setTemplateSummary(id, RemoteDataState.Loading)
try { 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())) dispatch(notify(copy.updateTemplateSucceeded()))
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -180,7 +121,7 @@ export const updateTemplate = (id: string, props: TemplateSummary) => async (
} }
export const convertToTemplate = (id: string) => async ( export const convertToTemplate = (id: string) => async (
dispatch dispatch: Dispatch<Action>
): Promise<void> => { ): Promise<void> => {
try { try {
dispatch(setExportTemplate(RemoteDataState.Loading)) 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)) dispatch(setExportTemplate(RemoteDataState.NotStarted, null))
} }
export const deleteTemplate = (templateID: string) => async ( export const deleteTemplate = (templateID: string) => async (
dispatch dispatch: Dispatch<Action>
): Promise<void> => { ): Promise<void> => {
try { try {
await client.templates.delete(templateID) await client.templates.delete(templateID)
@ -213,19 +154,19 @@ export const deleteTemplate = (templateID: string) => async (
} }
export const cloneTemplate = (templateID: string) => async ( export const cloneTemplate = (templateID: string) => async (
dispatch, dispatch: Dispatch<Action>,
getState: GetState getState: GetState
): Promise<void> => { ): Promise<void> => {
try { try {
const org = getOrg(getState()) const org = getOrg(getState())
const createdTemplate = await client.templates.clone(templateID, org.id) const createdTemplate = await client.templates.clone(templateID, org.id)
const templateSummary = normalize<
TemplateSummary,
TemplateSummaryEntities,
string
>(createdTemplate, schemas.template)
dispatch( dispatch(addTemplateSummary(templateSummary))
addTemplateSummary({
...createdTemplate,
labels: createdTemplate.labels || [],
})
)
dispatch(notify(copy.cloneTemplateSuccess())) dispatch(notify(copy.cloneTemplateSuccess()))
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -276,12 +217,19 @@ export const createResourceFromTemplate = (templateID: string) => async (
export const addTemplateLabelsAsync = ( export const addTemplateLabelsAsync = (
templateID: string, templateID: string,
labels: Label[] labels: Label[]
) => async (dispatch): Promise<void> => { ) => async (dispatch: Dispatch<Action>): Promise<void> => {
try { try {
await client.templates.addLabels(templateID, labels.map(l => l.id)) 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) { } catch (error) {
console.error(error) console.error(error)
dispatch(notify(copy.addTemplateLabelFailed())) dispatch(notify(copy.addTemplateLabelFailed()))
@ -291,20 +239,21 @@ export const addTemplateLabelsAsync = (
export const removeTemplateLabelsAsync = ( export const removeTemplateLabelsAsync = (
templateID: string, templateID: string,
labels: Label[] labels: Label[]
) => async (dispatch): Promise<void> => { ) => async (dispatch: Dispatch<Action>): Promise<void> => {
try { try {
await client.templates.removeLabels(templateID, labels.map(l => l.id)) 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) { } catch (error) {
console.error(error) console.error(error)
dispatch(notify(copy.removeTemplateLabelFailed())) 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' import {ResourceCard} from '@influxdata/clockface'
// Actions // Actions
import {createResourceFromStaticTemplate} from 'src/templates/actions' import {createResourceFromStaticTemplate} from 'src/templates/actions/thunks'
// Selectors // Selectors
import {viewableLabels} from 'src/labels/selectors' import {viewableLabels} from 'src/labels/selectors'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,36 +1,117 @@
import templatesReducer, {defaultState} from 'src/templates/reducers' // Libraries
import {setTemplateSummary} from 'src/templates/actions' import {normalize} from 'normalizr'
describe('templatesReducer', () => { // Schema
describe('setTemplateSummary', () => { import * as schemas from 'src/schemas'
it('can update the name of a template', () => {
const initialState = defaultState() // Reducer
const initialTemplate = { import {templatesReducer as reducer} from 'src/templates/reducers'
id: 'abc',
// 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: [], labels: [],
meta: {name: 'Belcalis', version: '1'}, status,
} }
initialState.items.push(initialTemplate)
const actual = templatesReducer( const exportTemplate = {status, item: null}
initialState,
setTemplateSummary(initialTemplate.id, { const initialState = () => ({
...initialTemplate, status,
meta: {...initialTemplate.meta, name: 'Cardi B'}, 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 = { const state = initialState()
...defaultState(),
items: [ const actual = reducer(state, addTemplateSummary(schema))
{
...initialTemplate, expect(actual.allIDs.length).toEqual(Number(id))
meta: {...initialTemplate.meta, name: 'Cardi B'}, })
},
], 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) 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 {produce} from 'immer'
import {Actions, ActionTypes} from 'src/templates/actions/' import {
import {TemplateSummary, DocumentCreate} from '@influxdata/influx' Action,
import {RemoteDataState} from 'src/types' ADD_TEMPLATE_SUMMARY,
POPULATE_TEMPLATE_SUMMARIES,
export interface TemplatesState { REMOVE_TEMPLATE_SUMMARY,
status: RemoteDataState SET_EXPORT_TEMPLATE,
items: TemplateSummary[] SET_TEMPLATE_SUMMARY,
exportTemplate: {status: RemoteDataState; item: DocumentCreate} 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 => ({ export const defaultState = (): TemplatesState => ({
status: RemoteDataState.NotStarted, status: RemoteDataState.NotStarted,
items: [], byID: {},
allIDs: [],
exportTemplate: { exportTemplate: {
status: RemoteDataState.NotStarted, status: RemoteDataState.NotStarted,
item: null, item: null,
@ -20,43 +33,34 @@ export const defaultState = (): TemplatesState => ({
export const templatesReducer = ( export const templatesReducer = (
state: TemplatesState = defaultState(), state: TemplatesState = defaultState(),
action: Actions action: Action
): TemplatesState => ): TemplatesState =>
produce(state, draftState => { produce(state, draftState => {
switch (action.type) { switch (action.type) {
case ActionTypes.PopulateTemplateSummaries: { case POPULATE_TEMPLATE_SUMMARIES: {
const {status, items} = action.payload setResource<TemplateSummary>(draftState, action, ResourceType.Templates)
draftState.status = status
if (items) {
draftState.items = items
} else {
draftState.items = null
}
return return
} }
case ActionTypes.SetTemplatesStatus: { case SET_TEMPLATES_STATUS: {
const {status} = action.payload const {status} = action
draftState.status = status draftState.status = status
return return
} }
case ActionTypes.SetTemplateSummary: { case SET_TEMPLATE_SUMMARY: {
const updated = draftState.items.map(t => { setResourceAtID<TemplateSummary>(
if (t.id === action.payload.id) { draftState,
return action.payload.templateSummary action,
} ResourceType.Templates
)
return t
})
draftState.items = updated
return return
} }
case ActionTypes.SetExportTemplate: { case SET_EXPORT_TEMPLATE: {
const {status, item} = action.payload const {status, item} = action
draftState.exportTemplate.status = status draftState.exportTemplate.status = status
if (item) { if (item) {
@ -67,21 +71,14 @@ export const templatesReducer = (
return return
} }
case ActionTypes.RemoveTemplateSummary: { case REMOVE_TEMPLATE_SUMMARY: {
const {templateID} = action.payload removeResource<TemplateSummary>(draftState, action)
const {items} = draftState
draftState.items = items.filter(l => {
return l.id !== templateID
})
return return
} }
case ActionTypes.AddTemplateSummary: { case ADD_TEMPLATE_SUMMARY: {
const {item} = action.payload addResource<TemplateSummary>(draftState, action, ResourceType.Templates)
const {items} = draftState
draftState.items = [...items, item]
return return
} }

View File

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

View File

@ -9,6 +9,7 @@ import {
Scraper, Scraper,
Task, Task,
Telegraf, Telegraf,
TemplateSummary,
Variable, Variable,
View, View,
} from 'src/types' } 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 // VariableEntities defines the result of normalizr's normalization
// of the "variables" resource // of the "variables" resource
export interface VariableEntities { export interface VariableEntities {

View File

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

View File

@ -1,10 +1,28 @@
import { import {
ILabel,
DocumentListEntry,
Document, Document,
DocumentCreate,
DocumentListEntry,
DocumentMeta, DocumentMeta,
ILabel,
} from '@influxdata/influx' } 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 { export enum TemplateType {
Label = 'label', Label = 'label',
@ -168,7 +186,3 @@ export interface VariableTemplate extends TemplateBase {
} }
export type Template = TaskTemplate | DashboardTemplate | VariableTemplate export type Template = TaskTemplate | DashboardTemplate | VariableTemplate
export interface TemplateSummary extends DocumentListEntry {
labels: ILabel[]
}

View File

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

View File

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