mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(editor): Don't show update notification for unverified updates (#18910)
Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
@@ -26,6 +26,12 @@
|
||||
"test:postgres": "N8N_LOG_LEVEL=silent DB_TYPE=postgresdb DB_POSTGRESDB_SCHEMA=alt_schema DB_TABLE_PREFIX=test_ jest --no-coverage",
|
||||
"test:mariadb": "N8N_LOG_LEVEL=silent DB_TYPE=mariadb DB_TABLE_PREFIX=test_ jest --no-coverage",
|
||||
"test:mysql": "N8N_LOG_LEVEL=silent DB_TYPE=mysqldb DB_TABLE_PREFIX=test_ jest --no-coverage",
|
||||
"test:win": "pnpm test:sqlite:win",
|
||||
"test:dev:win": "set N8N_LOG_LEVEL=silent&& set DB_TYPE=sqlite&& jest --watch",
|
||||
"test:sqlite:win": "set N8N_LOG_LEVEL=silent&& set DB_TYPE=sqlite&& jest",
|
||||
"test:postgres:win": "set N8N_LOG_LEVEL=silent&& set DB_TYPE=postgresdb&& set DB_POSTGRESDB_SCHEMA=alt_schema&& set DB_TABLE_PREFIX=test_&& jest --no-coverage",
|
||||
"test:mariadb:win": "set N8N_LOG_LEVEL=silent&& set DB_TYPE=mariadb&& set DB_TABLE_PREFIX=test_&& jest --no-coverage",
|
||||
"test:mysql:win": "set N8N_LOG_LEVEL=silent&& set DB_TYPE=mysqldb&& set DB_TABLE_PREFIX=test_&& jest --no-coverage",
|
||||
"watch": "tsc-watch -p tsconfig.build.json --onCompilationComplete \"tsc-alias -p tsconfig.build.json\""
|
||||
},
|
||||
"bin": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Logger } from '@n8n/backend-common';
|
||||
import { randomName, mockInstance } from '@n8n/backend-test-utils';
|
||||
import { mockInstance, randomName } from '@n8n/backend-test-utils';
|
||||
import { LICENSE_FEATURES } from '@n8n/constants';
|
||||
import axios from 'axios';
|
||||
import { mocked } from 'jest-mock';
|
||||
@@ -7,8 +7,8 @@ import { mock } from 'jest-mock-extended';
|
||||
import type { InstanceSettings, PackageDirectoryLoader } from 'n8n-core';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import { exec } from 'node:child_process';
|
||||
import { mkdir, readFile, writeFile, rm, access, constants } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { access, constants, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import path, { join } from 'node:path';
|
||||
|
||||
import {
|
||||
NODE_PACKAGE_PREFIX,
|
||||
@@ -54,7 +54,7 @@ describe('CommunityPackagesService', () => {
|
||||
const installedNodesRepository = mockInstance(InstalledNodesRepository);
|
||||
const installedPackageRepository = mockInstance(InstalledPackagesRepository);
|
||||
|
||||
const nodesDownloadDir = '/tmp/n8n-jest-global-downloads';
|
||||
const nodesDownloadDir = path.join('tmp', 'n8n-jest-global-downloads');
|
||||
const instanceSettings = mock<InstanceSettings>({ nodesDownloadDir });
|
||||
|
||||
const logger = mock<Logger>();
|
||||
@@ -441,7 +441,10 @@ describe('CommunityPackagesService', () => {
|
||||
// ASSERT:
|
||||
expect(rm).toHaveBeenCalledTimes(2);
|
||||
expect(rm).toHaveBeenNthCalledWith(1, testBlockPackageDir, { recursive: true, force: true });
|
||||
expect(rm).toHaveBeenNthCalledWith(2, `${nodesDownloadDir}/n8n-nodes-test-latest.tgz`);
|
||||
expect(rm).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
path.join(nodesDownloadDir, 'n8n-nodes-test-latest.tgz'),
|
||||
);
|
||||
|
||||
expect(exec).toHaveBeenCalledTimes(3);
|
||||
expect(exec).toHaveBeenNthCalledWith(
|
||||
@@ -672,7 +675,7 @@ describe('CommunityPackagesService', () => {
|
||||
await communityPackagesService.updatePackageJsonDependency('test-package', '1.0.0');
|
||||
|
||||
expect(writeFile).toHaveBeenCalledWith(
|
||||
`${nodesDownloadDir}/package.json`,
|
||||
path.join(nodesDownloadDir, 'package.json'),
|
||||
JSON.stringify({ dependencies: { 'test-package': '1.0.0' } }, null, 2),
|
||||
'utf-8',
|
||||
);
|
||||
@@ -682,7 +685,7 @@ describe('CommunityPackagesService', () => {
|
||||
await communityPackagesService.updatePackageJsonDependency('test-package', '1.0.0');
|
||||
|
||||
expect(writeFile).toHaveBeenCalledWith(
|
||||
`${nodesDownloadDir}/package.json`,
|
||||
path.join(nodesDownloadDir, 'package.json'),
|
||||
JSON.stringify({ dependencies: { 'test-package': '1.0.0' } }, null, 2),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
@@ -5,18 +5,16 @@ import { setActivePinia } from 'pinia';
|
||||
import CommunityNodeFooter from './CommunityNodeFooter.vue';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { vi } from 'vitest';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import { type ExtendedPublicInstalledPackage, fetchInstalledPackageInfo } from './utils';
|
||||
|
||||
const getInstalledPackage = vi.fn();
|
||||
vi.mock('./utils', () => ({
|
||||
fetchInstalledPackageInfo: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockedFetchInstalledPackageInfo = vi.mocked(fetchInstalledPackageInfo);
|
||||
|
||||
const push = vi.fn();
|
||||
|
||||
const communityNodesStore: {
|
||||
getInstalledPackage: (packageName: string) => Promise<PublicInstalledPackage>;
|
||||
} = {
|
||||
getInstalledPackage,
|
||||
};
|
||||
|
||||
vi.mock('vue-router', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
@@ -27,10 +25,6 @@ vi.mock('vue-router', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/stores/communityNodes.store', () => ({
|
||||
useCommunityNodesStore: vi.fn(() => communityNodesStore),
|
||||
}));
|
||||
|
||||
describe('CommunityNodeInfo - links & bugs URL', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia());
|
||||
@@ -70,25 +64,27 @@ describe('CommunityNodeInfo - links & bugs URL', () => {
|
||||
});
|
||||
|
||||
it('displays "Legacy" when updateAvailable', async () => {
|
||||
getInstalledPackage.mockResolvedValue({
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '1.0.0',
|
||||
updateAvailable: '1.0.1',
|
||||
} as PublicInstalledPackage);
|
||||
unverifiedUpdate: false,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
const { getByText } = createComponentRenderer(CommunityNodeFooter)({
|
||||
props: {
|
||||
packageName: 'n8n-nodes-test',
|
||||
showManage: false,
|
||||
},
|
||||
});
|
||||
await waitFor(() => expect(getInstalledPackage).toHaveBeenCalled());
|
||||
await waitFor(() => expect(mockedFetchInstalledPackageInfo).toHaveBeenCalled());
|
||||
|
||||
expect(getByText('Package version 1.0.0 (Legacy)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays "Latest" when not updateAvailable', async () => {
|
||||
getInstalledPackage.mockResolvedValue({
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '1.0.0',
|
||||
} as PublicInstalledPackage);
|
||||
unverifiedUpdate: false,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
const { getByText } = createComponentRenderer(CommunityNodeFooter)({
|
||||
props: {
|
||||
packageName: 'n8n-nodes-test',
|
||||
@@ -96,7 +92,24 @@ describe('CommunityNodeInfo - links & bugs URL', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => expect(getInstalledPackage).toHaveBeenCalled());
|
||||
await waitFor(() => expect(mockedFetchInstalledPackageInfo).toHaveBeenCalled());
|
||||
|
||||
expect(getByText('Package version 1.0.0 (Latest)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays "Latest" when only unverified update is available', async () => {
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '1.0.0',
|
||||
unverifiedUpdate: true,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
const { getByText } = createComponentRenderer(CommunityNodeFooter)({
|
||||
props: {
|
||||
packageName: 'n8n-nodes-test',
|
||||
showManage: false,
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => expect(mockedFetchInstalledPackageInfo).toHaveBeenCalled());
|
||||
|
||||
expect(getByText('Package version 1.0.0 (Latest)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { VIEWS } from '@/constants';
|
||||
import { captureException } from '@sentry/vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { captureException } from '@sentry/vue';
|
||||
import { useCommunityNodesStore } from '@/stores/communityNodes.store';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
|
||||
import { N8nText, N8nLink } from '@n8n/design-system';
|
||||
import { N8nLink, N8nText } from '@n8n/design-system';
|
||||
import { fetchInstalledPackageInfo, type ExtendedPublicInstalledPackage } from './utils';
|
||||
|
||||
export interface Props {
|
||||
packageName: string;
|
||||
@@ -17,7 +16,7 @@ const props = defineProps<Props>();
|
||||
const router = useRouter();
|
||||
|
||||
const bugsUrl = ref<string>(`https://registry.npmjs.org/${props.packageName}`);
|
||||
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
|
||||
const installedPackage = ref<ExtendedPublicInstalledPackage | undefined>(undefined);
|
||||
|
||||
async function openSettingsPage() {
|
||||
await router.push({ name: VIEWS.COMMUNITY_NODES });
|
||||
@@ -52,7 +51,7 @@ async function getBugsUrl(packageName: string) {
|
||||
onMounted(async () => {
|
||||
if (props.packageName) {
|
||||
await getBugsUrl(props.packageName);
|
||||
installedPackage.value = await useCommunityNodesStore().getInstalledPackage(props.packageName);
|
||||
installedPackage.value = await fetchInstalledPackageInfo(props.packageName);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -63,7 +62,9 @@ onMounted(async () => {
|
||||
<div :class="$style.container">
|
||||
<N8nText v-if="installedPackage" size="small" color="text-light" style="margin-right: auto">
|
||||
Package version {{ installedPackage.installedVersion }} ({{
|
||||
installedPackage.updateAvailable ? 'Legacy' : 'Latest'
|
||||
installedPackage.updateAvailable && !installedPackage.unverifiedUpdate
|
||||
? 'Legacy'
|
||||
: 'Latest'
|
||||
}})
|
||||
</N8nText>
|
||||
<template v-if="props.showManage">
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { type TestingPinia, createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import CommunityNodeInfo from './CommunityNodeInfo.vue';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import { waitFor } from '@testing-library/vue';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import type { CommunityNodeDetails } from '../composables/useViewStacks';
|
||||
import CommunityNodeInfo from './CommunityNodeInfo.vue';
|
||||
import { type ExtendedPublicInstalledPackage, fetchInstalledPackageInfo } from './utils';
|
||||
|
||||
vi.mock('./utils', () => ({
|
||||
fetchInstalledPackageInfo: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockedFetchInstalledPackageInfo = vi.mocked(fetchInstalledPackageInfo);
|
||||
|
||||
const getCommunityNodeAttributes = vi.fn();
|
||||
const getInstalledPackage = vi.fn();
|
||||
const communityNodesStore: {
|
||||
getInstalledPackage: (packageName: string) => Promise<PublicInstalledPackage>;
|
||||
} = {
|
||||
getInstalledPackage,
|
||||
};
|
||||
|
||||
vi.mock('@/stores/nodeTypes.store', () => ({
|
||||
useNodeTypesStore: vi.fn(() => ({
|
||||
@@ -26,10 +26,6 @@ vi.mock('@/stores/users.store', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@/stores/communityNodes.store', () => ({
|
||||
useCommunityNodesStore: vi.fn(() => communityNodesStore),
|
||||
}));
|
||||
|
||||
vi.mock('../composables/useViewStacks', () => ({
|
||||
useViewStacks: vi.fn(),
|
||||
}));
|
||||
@@ -98,10 +94,11 @@ describe('CommunityNodeInfo', () => {
|
||||
numberOfDownloads: 9999,
|
||||
nodeVersions: [{ npmVersion: '1.0.0' }],
|
||||
});
|
||||
getInstalledPackage.mockResolvedValue({
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '1.0.0',
|
||||
packageName: 'n8n-nodes-test',
|
||||
} as PublicInstalledPackage);
|
||||
unverifiedUpdate: false,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
|
||||
const wrapper = renderComponent({ pinia });
|
||||
|
||||
@@ -133,11 +130,12 @@ describe('CommunityNodeInfo', () => {
|
||||
numberOfDownloads: 9999,
|
||||
nodeVersions: [{ npmVersion: '1.0.0' }, { npmVersion: '0.0.9' }],
|
||||
});
|
||||
getInstalledPackage.mockResolvedValue({
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '0.0.9',
|
||||
packageName: 'n8n-nodes-test',
|
||||
updateAvailable: '1.0.1',
|
||||
} as PublicInstalledPackage);
|
||||
unverifiedUpdate: false,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
|
||||
const wrapper = renderComponent({ pinia });
|
||||
|
||||
@@ -154,6 +152,38 @@ describe('CommunityNodeInfo', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should NOT display update notice for unverified update', async () => {
|
||||
const { useViewStacks } = await import('../composables/useViewStacks');
|
||||
vi.mocked(useViewStacks).mockReturnValue({
|
||||
activeViewStack: {
|
||||
...defaultViewStack,
|
||||
communityNodeDetails: {
|
||||
...defaultViewStack.communityNodeDetails,
|
||||
installed: true,
|
||||
} as CommunityNodeDetails,
|
||||
},
|
||||
} as ReturnType<typeof useViewStacks>);
|
||||
|
||||
getCommunityNodeAttributes.mockResolvedValue({
|
||||
npmVersion: '1.0.0',
|
||||
authorName: 'contributor',
|
||||
numberOfDownloads: 9999,
|
||||
nodeVersions: [{ npmVersion: '1.0.0' }, { npmVersion: '0.0.9' }],
|
||||
});
|
||||
mockedFetchInstalledPackageInfo.mockResolvedValue({
|
||||
installedVersion: '0.0.9',
|
||||
packageName: 'n8n-nodes-test',
|
||||
updateAvailable: '1.0.1',
|
||||
unverifiedUpdate: true,
|
||||
} as ExtendedPublicInstalledPackage);
|
||||
|
||||
const wrapper = renderComponent({ pinia });
|
||||
|
||||
await waitFor(() => expect(wrapper.queryByTestId('number-of-downloads')).toBeInTheDocument());
|
||||
|
||||
expect(wrapper.queryByTestId('update-available')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correctly with fetched info', async () => {
|
||||
const packageData = {
|
||||
maintainers: [{ name: 'testAuthor' }],
|
||||
|
||||
@@ -4,11 +4,10 @@ import { useViewStacks } from '../composables/useViewStacks';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { i18n } from '@n8n/i18n';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useCommunityNodesStore } from '@/stores/communityNodes.store';
|
||||
import { captureException } from '@sentry/vue';
|
||||
import { N8nText, N8nTooltip, N8nIcon } from '@n8n/design-system';
|
||||
import ShieldIcon from 'virtual:icons/fa-solid/shield-alt';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import { type ExtendedPublicInstalledPackage, fetchInstalledPackageInfo } from './utils';
|
||||
|
||||
const { activeViewStack } = useViewStacks();
|
||||
|
||||
@@ -22,9 +21,8 @@ const publisherName = ref<string | undefined>(undefined);
|
||||
const downloads = ref<string | null>(null);
|
||||
const verified = ref(false);
|
||||
const official = ref(false);
|
||||
const installedPackage = ref<PublicInstalledPackage | undefined>(undefined);
|
||||
const installedPackage = ref<ExtendedPublicInstalledPackage | undefined>(undefined);
|
||||
|
||||
const communityNodesStore = useCommunityNodesStore();
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
|
||||
const isOwner = computed(() => useUsersStore().isInstanceOwner);
|
||||
@@ -46,9 +44,7 @@ async function fetchPackageInfo(packageName: string) {
|
||||
);
|
||||
|
||||
if (communityNodeDetails?.installed) {
|
||||
installedPackage.value = await communityNodesStore.getInstalledPackage(
|
||||
communityNodeDetails.packageName,
|
||||
);
|
||||
installedPackage.value = await fetchInstalledPackageInfo(communityNodeDetails.packageName);
|
||||
}
|
||||
|
||||
if (communityNodeAttributes) {
|
||||
@@ -116,7 +112,7 @@ onMounted(async () => {
|
||||
{{ communityNodeDetails?.description }}
|
||||
</N8nText>
|
||||
<CommunityNodeUpdateInfo
|
||||
v-if="isOwner && installedPackage?.updateAvailable"
|
||||
v-if="isOwner && installedPackage?.updateAvailable && !installedPackage.unverifiedUpdate"
|
||||
data-test-id="update-available"
|
||||
/>
|
||||
<div v-else :class="$style.separator"></div>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { fetchInstalledPackageInfo } from './utils';
|
||||
import { useCommunityNodesStore } from '@/stores/communityNodes.store';
|
||||
import { type NodeTypesStore, useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||
import type { CommunityNodeType } from '@n8n/api-types';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
|
||||
vi.mock('@/stores/communityNodes.store', () => ({
|
||||
useCommunityNodesStore: vi.fn(() => ({
|
||||
getInstalledPackage: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@/stores/nodeTypes.store', () => ({
|
||||
useNodeTypesStore: vi.fn(() => ({
|
||||
communityNodeType: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
type CommunityNodesStore = ReturnType<typeof useCommunityNodesStore>;
|
||||
|
||||
const mockCommunityNodesStore = (mock: Partial<CommunityNodesStore>) => {
|
||||
vi.mocked(useCommunityNodesStore).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
getInstalledPackage: vi.fn(),
|
||||
...mock,
|
||||
}) as unknown as CommunityNodesStore,
|
||||
);
|
||||
};
|
||||
|
||||
const mockNodeTypesStore = (mock: Partial<NodeTypesStore>) => {
|
||||
vi.mocked(useNodeTypesStore).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
communityNodeType: vi.fn(),
|
||||
...mock,
|
||||
}) as unknown as NodeTypesStore,
|
||||
);
|
||||
};
|
||||
|
||||
describe('fetchInstalledPackageInfo', () => {
|
||||
beforeEach(() => {
|
||||
createTestingPinia({ stubActions: false });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return undefined if no installed package is found', async () => {
|
||||
const packageName = 'test-package';
|
||||
mockCommunityNodesStore({
|
||||
getInstalledPackage: vi.fn().mockResolvedValue(undefined),
|
||||
});
|
||||
|
||||
const result = await fetchInstalledPackageInfo(packageName);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return package info with unverifiedUpdate as false if no update is available', async () => {
|
||||
const packageName = 'test-package';
|
||||
const installedPackage = { packageName, updateAvailable: null };
|
||||
mockCommunityNodesStore({
|
||||
getInstalledPackage: vi
|
||||
.fn()
|
||||
.mockResolvedValue(installedPackage as unknown as PublicInstalledPackage),
|
||||
});
|
||||
mockNodeTypesStore({
|
||||
communityNodeType: vi
|
||||
.fn()
|
||||
.mockReturnValue({ npmVersion: '1.0.0' } as unknown as CommunityNodeType),
|
||||
});
|
||||
|
||||
const result = await fetchInstalledPackageInfo(packageName);
|
||||
expect(result).toEqual({ ...installedPackage, unverifiedUpdate: false });
|
||||
});
|
||||
|
||||
it('should return package info with unverifiedUpdate as true if an update is available', async () => {
|
||||
const packageName = 'test-package';
|
||||
const installedPackage = { packageName, updateAvailable: '1.1.0' };
|
||||
mockCommunityNodesStore({
|
||||
getInstalledPackage: vi
|
||||
.fn()
|
||||
.mockResolvedValue(installedPackage as unknown as PublicInstalledPackage),
|
||||
});
|
||||
mockNodeTypesStore({
|
||||
communityNodeType: vi
|
||||
.fn()
|
||||
.mockReturnValue({ npmVersion: '1.0.0' } as unknown as CommunityNodeType),
|
||||
});
|
||||
|
||||
const result = await fetchInstalledPackageInfo(packageName);
|
||||
expect(result).toEqual({ ...installedPackage, unverifiedUpdate: true });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useCommunityNodesStore } from '@/stores/communityNodes.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { type PublicInstalledPackage } from 'n8n-workflow';
|
||||
import semver from 'semver';
|
||||
|
||||
export type ExtendedPublicInstalledPackage = PublicInstalledPackage & {
|
||||
unverifiedUpdate: boolean;
|
||||
};
|
||||
|
||||
export async function fetchInstalledPackageInfo(
|
||||
packageName: string,
|
||||
): Promise<ExtendedPublicInstalledPackage | undefined> {
|
||||
const installedPackage: PublicInstalledPackage | undefined =
|
||||
await useCommunityNodesStore().getInstalledPackage(packageName);
|
||||
const communityNodeType = useNodeTypesStore().communityNodeType(packageName);
|
||||
if (!installedPackage) {
|
||||
return undefined;
|
||||
}
|
||||
const checkIsUnverifiedUpdate = () => {
|
||||
if (!installedPackage?.updateAvailable || !communityNodeType) return false;
|
||||
return semver.gt(installedPackage.updateAvailable, communityNodeType.npmVersion);
|
||||
};
|
||||
|
||||
return { ...installedPackage, unverifiedUpdate: checkIsUnverifiedUpdate() };
|
||||
}
|
||||
Reference in New Issue
Block a user