mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-02 07:01:30 -08:00
fix(editor): Handle source control initialization to prevent UI form crashing (#11776)
This commit is contained in:
parent
f1ecd9b68c
commit
6be8e86c45
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import { RouterLink } from 'vue-router';
|
||||||
|
import { VIEWS } from '@/constants';
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<i18n-t keypath="settings.sourceControl.connection.error.message" tag="div">
|
||||||
|
<template #link>
|
||||||
|
<RouterLink :to="{ name: VIEWS.SOURCE_CONTROL }">
|
||||||
|
{{ i18n.baseText('settings.sourceControl.connection.error.link') }}
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
|
@ -8,6 +8,13 @@ import { createTestingPinia } from '@pinia/testing';
|
||||||
import { setActivePinia } from 'pinia';
|
import { setActivePinia } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useVersionsStore } from '@/stores/versions.store';
|
import { useVersionsStore } from '@/stores/versions.store';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
const showMessage = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('@/composables/useToast', () => ({
|
||||||
|
useToast: () => ({ showMessage }),
|
||||||
|
}));
|
||||||
|
|
||||||
vi.mock('@/stores/users.store', () => ({
|
vi.mock('@/stores/users.store', () => ({
|
||||||
useUsersStore: vi.fn().mockReturnValue({ initialize: vi.fn() }),
|
useUsersStore: vi.fn().mockReturnValue({ initialize: vi.fn() }),
|
||||||
|
@ -108,5 +115,21 @@ describe('Init', () => {
|
||||||
|
|
||||||
expect(cloudStoreSpy).toHaveBeenCalledTimes(1);
|
expect(cloudStoreSpy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle source control initialization error', async () => {
|
||||||
|
vi.mocked(useUsersStore).mockReturnValue({ currentUser: { id: '123' } } as ReturnType<
|
||||||
|
typeof useUsersStore
|
||||||
|
>);
|
||||||
|
vi.spyOn(sourceControlStore, 'getPreferences').mockRejectedValueOnce(
|
||||||
|
new AxiosError('Something went wrong', '404'),
|
||||||
|
);
|
||||||
|
const consoleSpy = vi.spyOn(window.console, 'error');
|
||||||
|
await initializeAuthenticatedFeatures(false);
|
||||||
|
expect(showMessage).toHaveBeenCalled();
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith(
|
||||||
|
'Failed to initialize source control store',
|
||||||
|
expect.anything(),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { h } from 'vue';
|
||||||
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useRootStore } from '@/stores/root.store';
|
import { useRootStore } from '@/stores/root.store';
|
||||||
|
@ -8,6 +9,9 @@ import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
import { useVersionsStore } from '@/stores/versions.store';
|
import { useVersionsStore } from '@/stores/versions.store';
|
||||||
import { useProjectsStore } from '@/stores/projects.store';
|
import { useProjectsStore } from '@/stores/projects.store';
|
||||||
import { useRolesStore } from './stores/roles.store';
|
import { useRolesStore } from './stores/roles.store';
|
||||||
|
import { useToast } from '@/composables/useToast';
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import SourceControlInitializationErrorMessage from '@/components/SourceControlInitializationErrorMessage.vue';
|
||||||
|
|
||||||
let coreInitialized = false;
|
let coreInitialized = false;
|
||||||
let authenticatedFeaturesInitialized = false;
|
let authenticatedFeaturesInitialized = false;
|
||||||
|
@ -41,8 +45,10 @@ export async function initializeCore() {
|
||||||
/**
|
/**
|
||||||
* Initializes the features of the application that require an authenticated user
|
* Initializes the features of the application that require an authenticated user
|
||||||
*/
|
*/
|
||||||
export async function initializeAuthenticatedFeatures() {
|
export async function initializeAuthenticatedFeatures(
|
||||||
if (authenticatedFeaturesInitialized) {
|
initialized: boolean = authenticatedFeaturesInitialized,
|
||||||
|
) {
|
||||||
|
if (initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +57,8 @@ export async function initializeAuthenticatedFeatures() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
|
const toast = useToast();
|
||||||
const sourceControlStore = useSourceControlStore();
|
const sourceControlStore = useSourceControlStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
@ -60,7 +68,17 @@ export async function initializeAuthenticatedFeatures() {
|
||||||
const rolesStore = useRolesStore();
|
const rolesStore = useRolesStore();
|
||||||
|
|
||||||
if (sourceControlStore.isEnterpriseSourceControlEnabled) {
|
if (sourceControlStore.isEnterpriseSourceControlEnabled) {
|
||||||
await sourceControlStore.getPreferences();
|
try {
|
||||||
|
await sourceControlStore.getPreferences();
|
||||||
|
} catch (e) {
|
||||||
|
toast.showMessage({
|
||||||
|
title: i18n.baseText('settings.sourceControl.connection.error'),
|
||||||
|
message: h(SourceControlInitializationErrorMessage),
|
||||||
|
type: 'error',
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
console.error('Failed to initialize source control store', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsStore.isTemplatesEnabled) {
|
if (settingsStore.isTemplatesEnabled) {
|
||||||
|
|
|
@ -1881,6 +1881,9 @@
|
||||||
"settings.sourceControl.actionBox.description": "Use multiple instances for different environments (dev, prod, etc.), deploying between them via a Git repository.",
|
"settings.sourceControl.actionBox.description": "Use multiple instances for different environments (dev, prod, etc.), deploying between them via a Git repository.",
|
||||||
"settings.sourceControl.actionBox.description.link": "More info",
|
"settings.sourceControl.actionBox.description.link": "More info",
|
||||||
"settings.sourceControl.actionBox.buttonText": "See plans",
|
"settings.sourceControl.actionBox.buttonText": "See plans",
|
||||||
|
"settings.sourceControl.connection.error": "Source control failed to connect",
|
||||||
|
"settings.sourceControl.connection.error.message": "We couldn't find the repository connected to this instance. Please double-check your {link} on this instance.",
|
||||||
|
"settings.sourceControl.connection.error.link": "Git configuration",
|
||||||
"settings.sourceControl.description": "Use multiple instances for different environments (dev, prod, etc.), deploying between them via a Git repository. {link}",
|
"settings.sourceControl.description": "Use multiple instances for different environments (dev, prod, etc.), deploying between them via a Git repository. {link}",
|
||||||
"settings.sourceControl.description.link": "More info",
|
"settings.sourceControl.description.link": "More info",
|
||||||
"settings.sourceControl.gitConfig": "Git configuration",
|
"settings.sourceControl.gitConfig": "Git configuration",
|
||||||
|
|
Loading…
Reference in a new issue