chore(community-templates): remove github file restriction
parent
eb2dd5af7b
commit
70b7287c9e
|
@ -32,19 +32,7 @@ describe('Community Templates', () => {
|
||||||
cy.getByTestID('lookup-template-button').click()
|
cy.getByTestID('lookup-template-button').click()
|
||||||
cy.getByTestID('notification-error').should('be.visible')
|
cy.getByTestID('notification-error').should('be.visible')
|
||||||
|
|
||||||
//lookup template errors on bad url
|
|
||||||
cy.getByTestID('lookup-template-input').type('www.badURL.com')
|
|
||||||
cy.getByTestID('lookup-template-button').click()
|
|
||||||
cy.getByTestID('notification-error').should('be.visible')
|
|
||||||
|
|
||||||
//lookup template errors on bad file type
|
|
||||||
cy.getByTestID('lookup-template-input').clear()
|
|
||||||
cy.getByTestID('lookup-template-input').type('variables.html')
|
|
||||||
cy.getByTestID('lookup-template-button').click()
|
|
||||||
cy.getByTestID('notification-error').should('be.visible')
|
|
||||||
|
|
||||||
//lookup template errors on github folder
|
//lookup template errors on github folder
|
||||||
cy.getByTestID('lookup-template-input').clear()
|
|
||||||
cy.getByTestID('lookup-template-input').type(
|
cy.getByTestID('lookup-template-input').type(
|
||||||
'https://github.com/influxdata/community-templates/tree/master/kafka'
|
'https://github.com/influxdata/community-templates/tree/master/kafka'
|
||||||
)
|
)
|
|
@ -13,8 +13,8 @@ export const ADD_TEMPLATE_SUMMARY = 'ADD_TEMPLATE_SUMMARY'
|
||||||
export const GET_TEMPLATE_SUMMARIES_FOR_ORG = 'GET_TEMPLATE_SUMMARIES_FOR_ORG'
|
export const GET_TEMPLATE_SUMMARIES_FOR_ORG = 'GET_TEMPLATE_SUMMARIES_FOR_ORG'
|
||||||
export const POPULATE_TEMPLATE_SUMMARIES = 'POPULATE_TEMPLATE_SUMMARIES'
|
export const POPULATE_TEMPLATE_SUMMARIES = 'POPULATE_TEMPLATE_SUMMARIES'
|
||||||
export const REMOVE_TEMPLATE_SUMMARY = 'REMOVE_TEMPLATE_SUMMARY'
|
export const REMOVE_TEMPLATE_SUMMARY = 'REMOVE_TEMPLATE_SUMMARY'
|
||||||
export const SET_COMMUNITY_TEMPLATE_TO_INSTALL =
|
export const SET_STAGED_TEMPLATE = 'SET_STAGED_TEMPLATE'
|
||||||
'SET_COMMUNITY_TEMPLATE_TO_INSTALL'
|
export const SET_STAGED_TEMPLATE_URL = 'SET_STAGED_TEMPLATE_URL'
|
||||||
export const SET_EXPORT_TEMPLATE = 'SET_EXPORT_TEMPLATE'
|
export const SET_EXPORT_TEMPLATE = 'SET_EXPORT_TEMPLATE'
|
||||||
export const SET_TEMPLATE_SUMMARY = 'SET_TEMPLATE_SUMMARY'
|
export const SET_TEMPLATE_SUMMARY = 'SET_TEMPLATE_SUMMARY'
|
||||||
export const SET_TEMPLATES_STATUS = 'SET_TEMPLATES_STATUS'
|
export const SET_TEMPLATES_STATUS = 'SET_TEMPLATES_STATUS'
|
||||||
|
@ -32,6 +32,7 @@ export type Action =
|
||||||
| ReturnType<typeof setTemplatesStatus>
|
| ReturnType<typeof setTemplatesStatus>
|
||||||
| ReturnType<typeof setTemplateSummary>
|
| ReturnType<typeof setTemplateSummary>
|
||||||
| ReturnType<typeof setStagedCommunityTemplate>
|
| ReturnType<typeof setStagedCommunityTemplate>
|
||||||
|
| ReturnType<typeof setStagedTemplateUrl>
|
||||||
| ReturnType<typeof toggleTemplateResourceInstall>
|
| ReturnType<typeof toggleTemplateResourceInstall>
|
||||||
| ReturnType<typeof setStacks>
|
| ReturnType<typeof setStacks>
|
||||||
| ReturnType<typeof removeStack>
|
| ReturnType<typeof removeStack>
|
||||||
|
@ -93,10 +94,16 @@ export const setTemplateSummary = (
|
||||||
|
|
||||||
export const setStagedCommunityTemplate = (template: CommunityTemplate) =>
|
export const setStagedCommunityTemplate = (template: CommunityTemplate) =>
|
||||||
({
|
({
|
||||||
type: SET_COMMUNITY_TEMPLATE_TO_INSTALL,
|
type: SET_STAGED_TEMPLATE,
|
||||||
template,
|
template,
|
||||||
} as const)
|
} as const)
|
||||||
|
|
||||||
|
export const setStagedTemplateUrl = (templateUrl: string) =>
|
||||||
|
({
|
||||||
|
type: SET_STAGED_TEMPLATE_URL,
|
||||||
|
templateUrl,
|
||||||
|
} as const)
|
||||||
|
|
||||||
export const toggleTemplateResourceInstall = (
|
export const toggleTemplateResourceInstall = (
|
||||||
resourceType: string,
|
resourceType: string,
|
||||||
templateMetaName: string,
|
templateMetaName: string,
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {ComponentStatus} from '@influxdata/clockface'
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {getByID} from 'src/resources/selectors'
|
import {getByID} from 'src/resources/selectors'
|
||||||
import {getGithubUrlFromTemplateDetails} from 'src/templates/utils'
|
import {getTemplateNameFromUrl} from 'src/templates/utils'
|
||||||
import {reportError} from 'src/shared/utils/errors'
|
import {reportError} from 'src/shared/utils/errors'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -41,11 +41,9 @@ interface State {
|
||||||
|
|
||||||
type ReduxProps = ConnectedProps<typeof connector>
|
type ReduxProps = ConnectedProps<typeof connector>
|
||||||
type RouterProps = RouteComponentProps<{
|
type RouterProps = RouteComponentProps<{
|
||||||
directory: string
|
|
||||||
orgID: string
|
orgID: string
|
||||||
templateName: string
|
|
||||||
templateExtension: string
|
|
||||||
}>
|
}>
|
||||||
|
|
||||||
type Props = ReduxProps & RouterProps
|
type Props = ReduxProps & RouterProps
|
||||||
|
|
||||||
class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
||||||
|
@ -54,12 +52,13 @@ class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
const {directory, org, templateExtension, templateName} = this.props
|
if (!this.props.stagedTemplateUrl) {
|
||||||
|
this.onDismiss()
|
||||||
|
return
|
||||||
|
}
|
||||||
this.reviewTemplateResources(
|
this.reviewTemplateResources(
|
||||||
org.id,
|
this.props.org.id,
|
||||||
directory,
|
this.props.stagedTemplateUrl
|
||||||
templateName,
|
|
||||||
templateExtension
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,26 +69,18 @@ class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
||||||
onInstall={this.handleInstallTemplate}
|
onInstall={this.handleInstallTemplate}
|
||||||
resourceCount={this.props.resourceCount}
|
resourceCount={this.props.resourceCount}
|
||||||
status={this.state.status}
|
status={this.state.status}
|
||||||
templateName={this.props.templateName}
|
templateName={getTemplateNameFromUrl(this.props.stagedTemplateUrl).name}
|
||||||
updateStatus={this.updateOverlayStatus}
|
updateStatus={this.updateOverlayStatus}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private reviewTemplateResources = async (
|
private reviewTemplateResources = async (
|
||||||
orgID,
|
orgID: string,
|
||||||
directory,
|
templateUrl: string
|
||||||
templateName,
|
|
||||||
templateExtension
|
|
||||||
) => {
|
) => {
|
||||||
const yamlLocation = getGithubUrlFromTemplateDetails(
|
|
||||||
directory,
|
|
||||||
templateName,
|
|
||||||
templateExtension
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const summary = await reviewTemplate(orgID, yamlLocation)
|
const summary = await reviewTemplate(orgID, templateUrl)
|
||||||
|
|
||||||
this.props.setStagedCommunityTemplate(summary)
|
this.props.setStagedCommunityTemplate(summary)
|
||||||
return summary
|
return summary
|
||||||
|
@ -102,28 +93,18 @@ class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDismiss = () => {
|
private onDismiss = () => {
|
||||||
const {history} = this.props
|
this.props.history.push(`/orgs/${this.props.org.id}/settings/templates`)
|
||||||
|
|
||||||
history.goBack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateOverlayStatus = (status: ComponentStatus) =>
|
private updateOverlayStatus = (status: ComponentStatus) =>
|
||||||
this.setState(() => ({status}))
|
this.setState(() => ({status}))
|
||||||
|
|
||||||
private handleInstallTemplate = async () => {
|
private handleInstallTemplate = async () => {
|
||||||
const {directory, org, templateExtension, templateName} = this.props
|
|
||||||
|
|
||||||
const yamlLocation = getGithubUrlFromTemplateDetails(
|
|
||||||
directory,
|
|
||||||
templateName,
|
|
||||||
templateExtension
|
|
||||||
)
|
|
||||||
|
|
||||||
let summary
|
let summary
|
||||||
try {
|
try {
|
||||||
summary = await installTemplate(
|
summary = await installTemplate(
|
||||||
org.id,
|
this.props.org.id,
|
||||||
yamlLocation,
|
this.props.stagedTemplateUrl,
|
||||||
this.props.resourcesToSkip
|
this.props.resourcesToSkip
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -132,16 +113,19 @@ class UnconnectedTemplateImportOverlay extends PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateStackName(summary.stackID, templateName)
|
const templateDetails = getTemplateNameFromUrl(
|
||||||
|
this.props.stagedTemplateUrl
|
||||||
|
)
|
||||||
|
await updateStackName(summary.stackID, templateDetails.name)
|
||||||
|
|
||||||
event('template_install', {templateName: templateName})
|
event('template_install', {templateName: templateDetails.name})
|
||||||
|
|
||||||
this.props.notify(communityTemplateInstallSucceeded(templateName))
|
this.props.notify(communityTemplateInstallSucceeded(templateDetails.name))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.notify(communityTemplateRenameFailed())
|
this.props.notify(communityTemplateRenameFailed())
|
||||||
reportError(err, {name: 'The community template rename failed'})
|
reportError(err, {name: 'The community template rename failed'})
|
||||||
} finally {
|
} finally {
|
||||||
this.props.fetchAndSetStacks(org.id)
|
this.props.fetchAndSetStacks(this.props.org.id)
|
||||||
this.onDismiss()
|
this.onDismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,15 +140,13 @@ const mstp = (state: AppState, props: RouterProps) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
org,
|
org,
|
||||||
directory: props.match.params.directory,
|
|
||||||
templateName: props.match.params.templateName,
|
|
||||||
templateExtension: props.match.params.templateExtension,
|
|
||||||
flags: state.flags.original,
|
flags: state.flags.original,
|
||||||
resourceCount: getTotalResourceCount(
|
resourceCount: getTotalResourceCount(
|
||||||
state.resources.templates.stagedCommunityTemplate.summary
|
state.resources.templates.stagedCommunityTemplate.summary
|
||||||
),
|
),
|
||||||
resourcesToSkip:
|
resourcesToSkip:
|
||||||
state.resources.templates.stagedCommunityTemplate.resourcesToSkip,
|
state.resources.templates.stagedCommunityTemplate.resourcesToSkip,
|
||||||
|
stagedTemplateUrl: state.resources.templates.stagedTemplateUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,15 +35,18 @@ import {communityTemplatesImportPath} from 'src/templates/containers/TemplatesIn
|
||||||
import GetResources from 'src/resources/components/GetResources'
|
import GetResources from 'src/resources/components/GetResources'
|
||||||
import {getOrg} from 'src/organizations/selectors'
|
import {getOrg} from 'src/organizations/selectors'
|
||||||
|
|
||||||
|
import {setStagedTemplateUrl} from 'src/templates/actions/creators'
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'
|
||||||
import {
|
import {
|
||||||
getGithubUrlFromTemplateDetails,
|
getGithubUrlFromTemplateDetails,
|
||||||
getTemplateDetails,
|
getTemplateNameFromUrl,
|
||||||
} from 'src/templates/utils'
|
} from 'src/templates/utils'
|
||||||
import {reportError} from 'src/shared/utils/errors'
|
import {reportError} from 'src/shared/utils/errors'
|
||||||
|
|
||||||
import {communityTemplateUnsupportedFormatError} from 'src/shared/copy/notifications'
|
import {communityTemplateUnsupportedFormatError} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import {AppState, ResourceType} from 'src/types'
|
import {AppState, ResourceType} from 'src/types'
|
||||||
|
|
||||||
|
@ -168,7 +171,7 @@ class UnconnectedTemplatesIndex extends Component<Props> {
|
||||||
</Page>
|
</Page>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
path={`${templatesPath}/import/:directory/:templateName/:templateExtension`}
|
path={`${templatesPath}/import`}
|
||||||
component={CommunityTemplateImportOverlay}
|
component={CommunityTemplateImportOverlay}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
@ -183,12 +186,14 @@ class UnconnectedTemplatesIndex extends Component<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {directory, templateExtension, templateName} = getTemplateDetails(
|
this.props.setStagedTemplateUrl(this.state.templateUrl)
|
||||||
this.state.templateUrl
|
|
||||||
)
|
event('template_click_lookup', {
|
||||||
event('template_click_lookup', {templateName: templateName})
|
templateName: getTemplateNameFromUrl(this.state.templateUrl).name,
|
||||||
|
})
|
||||||
|
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`/orgs/${this.props.org.id}/settings/templates/import/${directory}/${templateName}/${templateExtension}`
|
`/orgs/${this.props.org.id}/settings/templates/import`
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.notify(communityTemplateUnsupportedFormatError())
|
this.props.notify(communityTemplateUnsupportedFormatError())
|
||||||
|
@ -217,6 +222,7 @@ const mstp = (state: AppState) => {
|
||||||
|
|
||||||
const mdtp = {
|
const mdtp = {
|
||||||
notify,
|
notify,
|
||||||
|
setStagedTemplateUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
const connector = connect(mstp, mdtp)
|
const connector = connect(mstp, mdtp)
|
||||||
|
|
|
@ -46,6 +46,7 @@ const stagedCommunityTemplate: CommunityTemplate = {}
|
||||||
|
|
||||||
const initialState = () => ({
|
const initialState = () => ({
|
||||||
stagedCommunityTemplate,
|
stagedCommunityTemplate,
|
||||||
|
stagedTemplateUrl: '',
|
||||||
status,
|
status,
|
||||||
byID: {
|
byID: {
|
||||||
['1']: templateSummary,
|
['1']: templateSummary,
|
||||||
|
@ -99,6 +100,7 @@ describe('templates reducer', () => {
|
||||||
allIDs,
|
allIDs,
|
||||||
exportTemplate,
|
exportTemplate,
|
||||||
stagedCommunityTemplate,
|
stagedCommunityTemplate,
|
||||||
|
stagedTemplateUrl: '',
|
||||||
stacks: [],
|
stacks: [],
|
||||||
}
|
}
|
||||||
const actual = reducer(state, removeTemplateSummary(state.allIDs[1]))
|
const actual = reducer(state, removeTemplateSummary(state.allIDs[1]))
|
||||||
|
|
|
@ -4,12 +4,13 @@ import {
|
||||||
ADD_TEMPLATE_SUMMARY,
|
ADD_TEMPLATE_SUMMARY,
|
||||||
POPULATE_TEMPLATE_SUMMARIES,
|
POPULATE_TEMPLATE_SUMMARIES,
|
||||||
REMOVE_TEMPLATE_SUMMARY,
|
REMOVE_TEMPLATE_SUMMARY,
|
||||||
SET_COMMUNITY_TEMPLATE_TO_INSTALL,
|
|
||||||
SET_EXPORT_TEMPLATE,
|
SET_EXPORT_TEMPLATE,
|
||||||
SET_TEMPLATE_SUMMARY,
|
|
||||||
SET_TEMPLATES_STATUS,
|
|
||||||
TOGGLE_TEMPLATE_RESOURCE_INSTALL,
|
|
||||||
SET_STACKS,
|
SET_STACKS,
|
||||||
|
SET_STAGED_TEMPLATE,
|
||||||
|
SET_STAGED_TEMPLATE_URL,
|
||||||
|
SET_TEMPLATES_STATUS,
|
||||||
|
SET_TEMPLATE_SUMMARY,
|
||||||
|
TOGGLE_TEMPLATE_RESOURCE_INSTALL,
|
||||||
} from 'src/templates/actions/creators'
|
} from 'src/templates/actions/creators'
|
||||||
import {
|
import {
|
||||||
CommunityTemplate,
|
CommunityTemplate,
|
||||||
|
@ -37,6 +38,7 @@ const defaultCommunityTemplate = (): CommunityTemplate => {
|
||||||
|
|
||||||
export const defaultState = (): TemplatesState => ({
|
export const defaultState = (): TemplatesState => ({
|
||||||
stagedCommunityTemplate: defaultCommunityTemplate(),
|
stagedCommunityTemplate: defaultCommunityTemplate(),
|
||||||
|
stagedTemplateUrl: '',
|
||||||
status: RemoteDataState.NotStarted,
|
status: RemoteDataState.NotStarted,
|
||||||
byID: {},
|
byID: {},
|
||||||
allIDs: [],
|
allIDs: [],
|
||||||
|
@ -75,7 +77,14 @@ export const templatesReducer = (
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
case SET_COMMUNITY_TEMPLATE_TO_INSTALL: {
|
case SET_STAGED_TEMPLATE_URL: {
|
||||||
|
const {templateUrl} = action
|
||||||
|
|
||||||
|
draftState.stagedTemplateUrl = templateUrl
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case SET_STAGED_TEMPLATE: {
|
||||||
const {template} = action
|
const {template} = action
|
||||||
|
|
||||||
const stagedCommunityTemplate = {
|
const stagedCommunityTemplate = {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
getTemplateNameFromUrl,
|
||||||
findIncludedsFromRelationships,
|
findIncludedsFromRelationships,
|
||||||
findIncludedFromRelationship,
|
findIncludedFromRelationship,
|
||||||
findIncludedVariables,
|
findIncludedVariables,
|
||||||
|
@ -46,3 +47,45 @@ describe('Templates utils', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('the Community Template url utilities', () => {
|
||||||
|
it('returns the template name and extension from an arbitrary url', () => {
|
||||||
|
const {name, extension} = getTemplateNameFromUrl(
|
||||||
|
'https://github.com/influxdata/influxdb/blob/master/pkger/testdata/dashboard_params.yml'
|
||||||
|
)
|
||||||
|
expect(name).toBe('dashboard_params')
|
||||||
|
expect(extension).toBe('yml')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the template name and extension from the official community templates github repo', () => {
|
||||||
|
const {name, extension} = getTemplateNameFromUrl(
|
||||||
|
'https://github.com/influxdata/community-templates/blob/master/csgo/csgo.yml'
|
||||||
|
)
|
||||||
|
expect(name).toBe('csgo')
|
||||||
|
expect(extension).toBe('yml')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the template name and extension from the official community templates github repo when the extension is not yml', () => {
|
||||||
|
const {name, extension} = getTemplateNameFromUrl(
|
||||||
|
'https://github.com/influxdata/community-templates/blob/master/csgo/csgo.json'
|
||||||
|
)
|
||||||
|
expect(name).toBe('csgo')
|
||||||
|
expect(extension).toBe('json')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the template name and extension from arbitrary urls', () => {
|
||||||
|
const {name, extension} = getTemplateNameFromUrl(
|
||||||
|
'https://www.example.com/csgo/csgo.json'
|
||||||
|
)
|
||||||
|
expect(name).toBe('csgo')
|
||||||
|
expect(extension).toBe('json')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('handles non secure arbitrary urls', () => {
|
||||||
|
const {name, extension} = getTemplateNameFromUrl(
|
||||||
|
'http://www.example.com/blog/cats/catstuff/memes/csgo/downsampling.yml'
|
||||||
|
)
|
||||||
|
expect(name).toBe('downsampling')
|
||||||
|
expect(extension).toBe('yml')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -101,6 +101,14 @@ const getTemplateDetailsFromFileSource = (_source: string): TemplateDetails => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getTemplateNameFromUrl = (
|
||||||
|
url: string
|
||||||
|
): {name: string; extension: string} => {
|
||||||
|
const fullName = url.split('/').pop()
|
||||||
|
const [name, extension] = fullName.split('.')
|
||||||
|
return {name, extension}
|
||||||
|
}
|
||||||
|
|
||||||
export const getTemplateDetails = (source: string): TemplateDetails => {
|
export const getTemplateDetails = (source: string): TemplateDetails => {
|
||||||
if (source.includes('https')) {
|
if (source.includes('https')) {
|
||||||
return getTemplateDetailsFromGithubSource(source)
|
return getTemplateDetailsFromGithubSource(source)
|
||||||
|
|
|
@ -36,6 +36,7 @@ export type CommunityTemplate = any
|
||||||
export interface TemplatesState extends NormalizedState<TemplateSummary> {
|
export interface TemplatesState extends NormalizedState<TemplateSummary> {
|
||||||
exportTemplate: {status: RemoteDataState; item: DocumentCreate}
|
exportTemplate: {status: RemoteDataState; item: DocumentCreate}
|
||||||
stagedCommunityTemplate: CommunityTemplate
|
stagedCommunityTemplate: CommunityTemplate
|
||||||
|
stagedTemplateUrl: string
|
||||||
stacks: InstalledStack[]
|
stacks: InstalledStack[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue