Skip to content

Commit

Permalink
feat(editor): Open template credential setup from collection (#7882)
Browse files Browse the repository at this point in the history
Open the template credential setup when using a template from the
collection view.

NOTE! This feature is still behind a feature flag. To test, set:

```js
localStorage.setItem('template-credentials-setup', 'true')
```


https://github.com/n8n-io/n8n/assets/10324676/46ccec7c-5a44-429e-99ad-1c10640e99e5
  • Loading branch information
tomi authored Nov 30, 2023
1 parent 67702c2 commit 627ddb9
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 49 deletions.
15 changes: 15 additions & 0 deletions cypress/e2e/34-template-credentials-setup.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CredentialsModal, MessageBox } from '../pages/modals';
import {
clickUseWorkflowButtonByTitle,
visitTemplateCollectionPage,
testData,
} from '../pages/template-collection';
import { TemplateCredentialSetupPage } from '../pages/template-credential-setup';
import { TemplateWorkflowPage } from '../pages/template-workflow';
import { WorkflowPage } from '../pages/workflow';
Expand Down Expand Up @@ -28,6 +33,16 @@ describe('Template credentials setup', () => {
.should('be.visible');
});

it('can be opened from template collection page', () => {
visitTemplateCollectionPage(testData.ecommerceStarterPack);
templateCredentialsSetupPage.actions.enableFeatureFlag();
clickUseWorkflowButtonByTitle('Promote new Shopify products on Twitter and Telegram');

templateCredentialsSetupPage.getters
.title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`)
.should('be.visible');
});

it('can be opened with a direct url', () => {
templateCredentialsSetupPage.actions.visit(testTemplate.id);

Expand Down

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions cypress/pages/template-collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function visitTemplateCollectionPage(withFixture: Fixture) {
cy.intercept(
'GET',
`https://api.n8n.io/api/templates/collections/${testData.ecommerceStarterPack.id}`,
{
fixture: withFixture.fixture,
},
).as('getTemplateCollection');

cy.visit(`/collections/${withFixture.id}`);

cy.wait('@getTemplateCollection');
}

export function clickUseWorkflowButtonByTitle(workflowTitle: string) {
cy.getByTestId('template-card')
.contains('[data-test-id=template-card]', workflowTitle)
.realHover({ position: 'center' })
.findChildByTestId('use-workflow-button')
.click({ force: true });
}

export type Fixture = {
id: number;
fixture: string;
};

export const testData = {
ecommerceStarterPack: {
id: 1,
fixture: 'Ecommerce_starter_pack_template_collection.json',
},
};
2 changes: 2 additions & 0 deletions packages/editor-ui/src/components/TemplateCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
!loading && $style.loaded,
]"
@click="onCardClick"
data-test-id="template-card"
>
<div :class="$style.loading" v-if="loading">
<n8n-loading :rows="2" :shrinkLast="false" :loading="loading" />
Expand Down Expand Up @@ -39,6 +40,7 @@
outline
label="Use workflow"
@click.stop="onUseWorkflowClick"
data-test-id="use-workflow-button"
/>
</div>
</div>
Expand Down
4 changes: 1 addition & 3 deletions packages/editor-ui/src/stores/templates.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,7 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
this.currentSessionId = `templates-${Date.now()}`;
}
},
async fetchTemplateById(
templateId: string,
): Promise<ITemplatesWorkflow | ITemplatesWorkflowFull> {
async fetchTemplateById(templateId: string): Promise<ITemplatesWorkflowFull> {
const settingsStore = useSettingsStore();
const apiEndpoint: string = settingsStore.templatesHost;
const versionCli: string = settingsStore.versionCli;
Expand Down
32 changes: 32 additions & 0 deletions packages/editor-ui/src/utils/templates/templateActions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { INodeUi, IWorkflowData, IWorkflowTemplate } from '@/Interface';
import { getNewWorkflow } from '@/api/workflows';
import { VIEWS } from '@/constants';
import type { useRootStore } from '@/stores/n8nRoot.store';
import type { useWorkflowsStore } from '@/stores/workflows.store';
import { FeatureFlag, isFeatureFlagEnabled } from '@/utils/featureFlag';
import { getFixedNodesList } from '@/utils/nodeViewUtils';
import { replaceAllTemplateNodeCredentials } from '@/utils/templates/templateTransforms';
import type { INodeCredentialsDetails } from 'n8n-workflow';
import type { RouteLocationRaw, Router } from 'vue-router';

/**
* Creates a new workflow from a template
Expand Down Expand Up @@ -35,3 +38,32 @@ export async function createWorkflowFromTemplate(

return createdWorkflow;
}

/**
* Opens the template credential setup view (or workflow view
* if the feature flag is disabled)
*/
export async function openTemplateCredentialSetup(opts: {
templateId: string;
router: Router;
inNewBrowserTab?: boolean;
}) {
const { router, templateId, inNewBrowserTab = false } = opts;

const routeLocation: RouteLocationRaw = isFeatureFlagEnabled(FeatureFlag.templateCredentialsSetup)
? {
name: VIEWS.TEMPLATE_SETUP,
params: { id: templateId },
}
: {
name: VIEWS.TEMPLATE_IMPORT,
params: { id: templateId },
};

if (inNewBrowserTab) {
const route = router.resolve(routeLocation);
window.open(route.href, '_blank');
} else {
await router.push(routeLocation);
}
}
35 changes: 23 additions & 12 deletions packages/editor-ui/src/views/TemplatesCollectionView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import { setPageTitle } from '@/utils/htmlUtils';
import { VIEWS } from '@/constants';
import { useTemplatesStore } from '@/stores/templates.store';
import { usePostHog } from '@/stores/posthog.store';
import { FeatureFlag, isFeatureFlagEnabled } from '@/utils/featureFlag';
import { openTemplateCredentialSetup } from '@/utils/templates/templateActions';
export default defineComponent({
name: 'TemplatesCollectionView',
Expand All @@ -80,11 +82,13 @@ export default defineComponent({
},
computed: {
...mapStores(useTemplatesStore, usePostHog),
collection(): null | ITemplatesCollectionFull {
collection(): ITemplatesCollectionFull | null {
return this.templatesStore.getCollectionById(this.collectionId);
},
collectionId(): string {
return this.$route.params.id;
return Array.isArray(this.$route.params.id)
? this.$route.params.id[0]
: this.$route.params.id;
},
collectionWorkflows(): Array<ITemplatesWorkflow | ITemplatesWorkflowFull | null> | null {
if (!this.collection) {
Expand Down Expand Up @@ -116,17 +120,24 @@ export default defineComponent({
onOpenTemplate({ event, id }: { event: MouseEvent; id: string }) {
this.navigateTo(event, VIEWS.TEMPLATE, id);
},
onUseWorkflow({ event, id }: { event: MouseEvent; id: string }) {
const telemetryPayload = {
template_id: id,
wf_template_repo_session_id: this.workflowsStore.currentSessionId,
source: 'collection',
};
void this.$externalHooks().run('templatesCollectionView.onUseWorkflow', telemetryPayload);
this.$telemetry.track('User inserted workflow template', telemetryPayload, {
withPostHog: true,
async onUseWorkflow({ event, id }: { event: MouseEvent; id: string }) {
if (!isFeatureFlagEnabled(FeatureFlag.templateCredentialsSetup)) {
const telemetryPayload = {
template_id: id,
wf_template_repo_session_id: this.templatesStore.currentSessionId,
source: 'collection',
};
await this.$externalHooks().run('templatesCollectionView.onUseWorkflow', telemetryPayload);
this.$telemetry.track('User inserted workflow template', telemetryPayload, {
withPostHog: true,
});
}
await openTemplateCredentialSetup({
router: this.$router,
templateId: id,
inNewBrowserTab: event.metaKey || event.ctrlKey,
});
this.navigateTo(event, VIEWS.TEMPLATE_IMPORT, id);
},
navigateTo(e: MouseEvent, page: string, id: string) {
if (e.metaKey || e.ctrlKey) {
Expand Down
60 changes: 26 additions & 34 deletions packages/editor-ui/src/views/TemplatesWorkflowView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
data-test-id="use-template-button"
:label="$locale.baseText('template.buttons.useThisWorkflowButton')"
size="large"
@click="openTemplateSetup(template.id, $event)"
@click="openTemplateSetup(templateId, $event)"
/>
<n8n-loading :loading="!template" :rows="1" variant="button" />
</div>
Expand Down Expand Up @@ -63,12 +63,12 @@ import TemplateDetails from '@/components/TemplateDetails.vue';
import TemplatesView from './TemplatesView.vue';
import WorkflowPreview from '@/components/WorkflowPreview.vue';
import type { ITemplatesWorkflow, ITemplatesWorkflowFull } from '@/Interface';
import type { ITemplatesWorkflowFull } from '@/Interface';
import { workflowHelpers } from '@/mixins/workflowHelpers';
import { setPageTitle } from '@/utils/htmlUtils';
import { VIEWS } from '@/constants';
import { useTemplatesStore } from '@/stores/templates.store';
import { usePostHog } from '@/stores/posthog.store';
import { openTemplateCredentialSetup } from '@/utils/templates/templateActions';
import { FeatureFlag, isFeatureFlagEnabled } from '@/utils/featureFlag';
export default defineComponent({
Expand All @@ -81,11 +81,13 @@ export default defineComponent({
},
computed: {
...mapStores(useTemplatesStore, usePostHog),
template(): ITemplatesWorkflow | ITemplatesWorkflowFull {
return this.templatesStore.getTemplateById(this.templateId);
template(): ITemplatesWorkflowFull | null {
return this.templatesStore.getFullTemplateById(this.templateId);
},
templateId() {
return this.$route.params.id;
return Array.isArray(this.$route.params.id)
? this.$route.params.id[0]
: this.$route.params.id;
},
},
data() {
Expand All @@ -96,35 +98,25 @@ export default defineComponent({
};
},
methods: {
openTemplateSetup(id: string, e: PointerEvent) {
const telemetryPayload = {
source: 'workflow',
template_id: id,
wf_template_repo_session_id: this.templatesStore.currentSessionId,
};
void this.$externalHooks().run('templatesWorkflowView.openWorkflow', telemetryPayload);
this.$telemetry.track('User inserted workflow template', telemetryPayload, {
withPostHog: true,
});
if (isFeatureFlagEnabled(FeatureFlag.templateCredentialsSetup)) {
if (e.metaKey || e.ctrlKey) {
const route = this.$router.resolve({ name: VIEWS.TEMPLATE_SETUP, params: { id } });
window.open(route.href, '_blank');
return;
} else {
void this.$router.push({ name: VIEWS.TEMPLATE_SETUP, params: { id } });
}
} else {
if (e.metaKey || e.ctrlKey) {
const route = this.$router.resolve({ name: VIEWS.TEMPLATE_IMPORT, params: { id } });
window.open(route.href, '_blank');
return;
} else {
void this.$router.push({ name: VIEWS.TEMPLATE_IMPORT, params: { id } });
}
async openTemplateSetup(id: string, e: PointerEvent) {
if (!isFeatureFlagEnabled(FeatureFlag.templateCredentialsSetup)) {
const telemetryPayload = {
source: 'workflow',
template_id: id,
wf_template_repo_session_id: this.templatesStore.currentSessionId,
};
this.$telemetry.track('User inserted workflow template', telemetryPayload, {
withPostHog: true,
});
await this.$externalHooks().run('templatesWorkflowView.openWorkflow', telemetryPayload);
}
await openTemplateCredentialSetup({
router: this.$router,
templateId: id,
inNewBrowserTab: e.metaKey || e.ctrlKey,
});
},
onHidePreview() {
this.showPreview = false;
Expand Down

0 comments on commit 627ddb9

Please sign in to comment.