chore(community-templates): remove github file restriction

pull/19425/head
Bucky Schwarz 2020-08-24 11:04:54 -07:00 committed by Bucky Schwarz
parent eb2dd5af7b
commit 70b7287c9e
9 changed files with 114 additions and 68 deletions

View File

@ -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'
) )

View File

@ -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,

View File

@ -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,
} }
} }

View File

@ -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)

View File

@ -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]))

View File

@ -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 = {

View File

@ -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')
})
})

View File

@ -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)

View File

@ -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[]
} }