refactor(editor): Fix remaining FE type check errors (no-changelog) (#9607)

Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
Ricardo Espinoza
2024-06-10 09:23:06 -04:00
committed by GitHub
parent 1e15f73b0d
commit 22bdb0568e
84 changed files with 438 additions and 318 deletions

View File

@@ -1115,8 +1115,6 @@ export default defineComponent({
oauthTokenData: {} as CredentialInformation,
};
this.credentialsStore.enableOAuthCredential(credential);
// Close the window
if (oauthPopup) {
oauthPopup.close();
@@ -1164,7 +1162,7 @@ export default defineComponent({
this.credentialData = {
...this.credentialData,
scopes,
scopes: scopes as unknown as CredentialInformation,
homeProject,
};
},

View File

@@ -72,7 +72,7 @@
</template>
<script lang="ts">
import type { ICredentialsResponse, IUser, IUserListAction } from '@/Interface';
import type { ICredentialsResponse, IUserListAction } from '@/Interface';
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import { useMessage } from '@/composables/useMessage';
@@ -157,22 +157,35 @@ export default defineComponent({
credentialOwnerName(): string {
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);
},
credentialDataHomeProject(): ProjectSharingData | undefined {
const credentialContainsProjectSharingData = (
data: ICredentialDataDecryptedObject,
): data is { homeProject: ProjectSharingData } => {
return 'homeProject' in data;
};
return this.credentialData && credentialContainsProjectSharingData(this.credentialData)
? this.credentialData.homeProject
: undefined;
},
isCredentialSharedWithCurrentUser(): boolean {
return (this.credentialData.sharedWithProjects ?? []).some((sharee: IUser) => {
return sharee.id === this.usersStore.currentUser?.id;
if (!Array.isArray(this.credentialData.sharedWithProjects)) return false;
return this.credentialData.sharedWithProjects.some((sharee) => {
return typeof sharee === 'object' && 'id' in sharee
? sharee.id === this.usersStore.currentUser?.id
: false;
});
},
projects(): ProjectListItem[] {
return this.projectsStore.personalProjects.filter(
(project) =>
project.id !== this.credential?.homeProject?.id &&
project.id !== this.credentialData?.homeProject?.id,
project.id !== this.credentialDataHomeProject?.id,
);
},
homeProject(): ProjectSharingData | undefined {
return (
this.credential?.homeProject ?? (this.credentialData?.homeProject as ProjectSharingData)
);
return this.credential?.homeProject ?? this.credentialDataHomeProject;
},
isHomeTeamProject(): boolean {
return this.homeProject?.type === ProjectTypes.Team;

View File

@@ -2,7 +2,7 @@
<div
:class="$style.wrapper"
:style="iconStyleData"
@click="(e) => $emit('click')"
@click="() => $emit('click')"
@mouseover="showTooltip = true"
@mouseleave="showTooltip = false"
>
@@ -126,7 +126,7 @@ export default defineComponent({
const restUrl = this.rootStore.getRestUrl;
if (nodeType.icon) {
if (typeof nodeType.icon === 'string') {
const [type, path] = nodeType.icon.split(':');
const returnData: NodeIconData = {
type,

View File

@@ -207,6 +207,10 @@ const isWorkflowHistoryButtonDisabled = computed(() => {
return isNewWorkflow.value;
});
const workflowTagIds = computed(() => {
return (props.workflow.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id));
});
watch(
() => props.workflow.id,
() => {
@@ -601,7 +605,7 @@ function showCreateWorkflowSuccessToast(id?: string) {
<TagsContainer
v-else
:key="workflow.id"
:tag-ids="workflow.tags"
:tag-ids="workflowTagIds"
:clickable="true"
:responsive="true"
data-test-id="workflow-tags"

View File

@@ -88,7 +88,9 @@ function onSelected(item: INodeCreateElement) {
const icon = item.properties.iconUrl
? `${baseUrl}${item.properties.iconUrl}`
: item.properties.icon?.split(':')[1];
: typeof item.properties.icon === 'string'
? item.properties.icon?.split(':')[1]
: undefined;
const transformedActions = nodeActions?.map((a) =>
transformNodeType(a, item.properties.displayName, 'action'),

View File

@@ -18,7 +18,7 @@ export const mockSimplifiedNodeType = (
): SimplifiedNodeType => ({
displayName: 'Sample DisplayName',
name: 'sampleName',
icon: 'sampleIcon',
icon: 'fa:sampleIcon',
iconUrl: 'https://example.com/icon.png',
group: ['group1', 'group2'],
description: 'Sample description',

View File

@@ -36,7 +36,7 @@ import { useI18n } from '@/composables/useI18n';
import { useKeyboardNavigation } from './useKeyboardNavigation';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import type { INodeInputFilter, NodeConnectionType } from 'n8n-workflow';
import type { INodeInputFilter, NodeConnectionType, Themed } from 'n8n-workflow';
import { useCanvasStore } from '@/stores/canvas.store';
interface ViewStack {
@@ -48,7 +48,7 @@ interface ViewStack {
info?: string;
nodeIcon?: {
iconType?: string;
icon?: string;
icon?: Themed<string>;
color?: string;
};
iconUrl?: string;

View File

@@ -58,7 +58,7 @@ import {
import { useI18n } from '@/composables/useI18n';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import type { SimplifiedNodeType } from '@/Interface';
import type { INodeTypeDescription } from 'n8n-workflow';
import type { INodeTypeDescription, Themed } from 'n8n-workflow';
import { NodeConnectionType } from 'n8n-workflow';
import { useTemplatesStore } from '@/stores/templates.store';
@@ -74,7 +74,7 @@ export interface NodeViewItem {
properties: {
name?: string;
title?: string;
icon?: string;
icon?: Themed<string>;
iconProps?: {
color?: string;
};

View File

@@ -16,7 +16,7 @@
</template>
<script setup lang="ts">
import type { IVersionNode } from '@/Interface';
import type { IVersionNode, SimplifiedNodeType } from '@/Interface';
import { useRootStore } from '@/stores/n8nRoot.store';
import { useUIStore } from '@/stores/ui.store';
import { getBadgeIconUrl, getNodeIcon, getNodeIconUrl } from '@/utils/nodeTypesUtils';
@@ -30,7 +30,7 @@ interface NodeIconSource {
}
type Props = {
nodeType?: INodeTypeDescription | IVersionNode | null;
nodeType?: INodeTypeDescription | SimplifiedNodeType | IVersionNode | null;
size?: number;
disabled?: boolean;
circle?: boolean;

View File

@@ -460,7 +460,6 @@ export default defineComponent({
parameters: {},
} as INodeParameters,
nodeValuesInitialized: false, // Used to prevent nodeValues from being overwritten by defaults on reopening ndv
nodeSettings: [] as INodeProperties[],
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
CUSTOM_NODES_DOCS_URL,
@@ -469,7 +468,7 @@ export default defineComponent({
};
},
watch: {
node(newNode, oldNode) {
node() {
this.setNodeValues();
},
},

View File

@@ -28,7 +28,7 @@ import { isCommunityPackageName } from '@/utils/nodeTypesUtils';
type Tab = 'settings' | 'params';
type Props = {
modelValue?: Tab;
nodeType?: INodeTypeDescription;
nodeType?: INodeTypeDescription | null;
pushRef?: string;
};

View File

@@ -1,6 +1,7 @@
import { createComponentRenderer } from '@/__tests__/render';
import ProjectTabs from '@/components/Projects/ProjectTabs.vue';
import { useRoute, useRouter } from 'vue-router';
import { createTestProject } from '@/__tests__/data/projects';
import { useProjectsStore } from '@/stores/projects.store';
vi.mock('vue-router', () => {
@@ -54,15 +55,14 @@ describe('ProjectTabs', () => {
it('should render project tabs if use has permissions', () => {
route.params.projectId = '123';
projectsStore.currentProject = {
id: '123',
type: 'team',
name: 'Project',
relations: [],
scopes: ['project:update'],
createdAt: '',
updatedAt: '',
};
vi.mocked(useProjectsStore).mockImplementationOnce(
() =>
({
currentProject: createTestProject({
scopes: ['project:update'],
}),
}) as ReturnType<typeof useProjectsStore>,
);
const { getByText } = renderComponent();
expect(getByText('Workflows')).toBeInTheDocument();
@@ -72,15 +72,14 @@ describe('ProjectTabs', () => {
it('should render project tabs', () => {
route.params.projectId = '123';
projectsStore.currentProject = {
id: '123',
type: 'team',
name: 'Project',
relations: [],
scopes: ['project:read'],
createdAt: '',
updatedAt: '',
};
vi.mocked(useProjectsStore).mockImplementationOnce(
() =>
({
currentProject: createTestProject({
scopes: ['project:read'],
}),
}) as ReturnType<typeof useProjectsStore>,
);
const { queryByText, getByText } = renderComponent();
expect(getByText('Workflows')).toBeInTheDocument();

View File

@@ -486,7 +486,7 @@ export default defineComponent({
this.eventBus.on('refreshList', this.refreshList);
window.addEventListener('resize', this.setWidth);
useNDVStore().$subscribe((mutation, state) => {
useNDVStore().$subscribe((_mutation, _state) => {
// Update the width when main panel dimension change
this.setWidth();
});

View File

@@ -16,7 +16,7 @@ import MappingModeSelect from './MappingModeSelect.vue';
import MatchingColumnsSelect from './MatchingColumnsSelect.vue';
import MappingFields from './MappingFields.vue';
import { fieldCannotBeDeleted, parseResourceMapperFieldName } from '@/utils/nodeTypesUtils';
import { isResourceMapperValue } from '@/utils/typeGuards';
import { isFullExecutionResponse, isResourceMapperValue } from '@/utils/typeGuards';
import { i18n as locale } from '@/plugins/i18n';
import { useNDVStore } from '@/stores/ndv.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
@@ -78,7 +78,12 @@ watch(
watch(
() => workflowsStore.getWorkflowExecution,
async (data) => {
if (data?.status === 'success' && state.paramValue.mappingMode === 'autoMapInputData') {
if (
data &&
isFullExecutionResponse(data) &&
data.status === 'success' &&
state.paramValue.mappingMode === 'autoMapInputData'
) {
await initFetching(true);
}
},

View File

@@ -176,12 +176,14 @@ import ParameterInputList from '@/components/ParameterInputList.vue';
import type { IMenuItem, INodeUi, IUpdateInformation } from '@/Interface';
import type {
IDataObject,
INodeCredentials,
NodeParameterValue,
MessageEventBusDestinationOptions,
INodeParameters,
NodeParameterValueType,
} from 'n8n-workflow';
import {
deepCopy,
messageEventBusDestinationTypeNames,
defaultMessageEventBusDestinationOptions,
defaultMessageEventBusDestinationWebhookOptions,
MessageEventBusDestinationTypeNames,
@@ -246,7 +248,7 @@ export default defineComponent({
showRemoveConfirm: false,
typeSelectValue: '',
typeSelectPlaceholder: 'Destination Type',
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions),
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions) as INodeParameters,
webhookDescription: webhookModalDescription,
sentryDescription: sentryModalDescription,
syslogDescription: syslogModalDescription,
@@ -261,7 +263,7 @@ export default defineComponent({
...mapStores(useUIStore, useLogStreamingStore, useNDVStore, useWorkflowsStore),
typeSelectOptions(): Array<{ value: string; label: BaseTextKey }> {
const options: Array<{ value: string; label: BaseTextKey }> = [];
for (const t of Object.values(MessageEventBusDestinationTypeNames)) {
for (const t of messageEventBusDestinationTypeNames) {
if (t === MessageEventBusDestinationTypeNames.abstract) {
continue;
}
@@ -325,7 +327,8 @@ export default defineComponent({
if (arg.name === this.destination.id) {
if ('credentials' in arg.properties) {
this.unchanged = false;
this.nodeParameters.credentials = arg.properties.credentials as INodeCredentials;
this.nodeParameters.credentials = arg.properties
.credentials as NodeParameterValueType;
}
}
}
@@ -350,7 +353,7 @@ export default defineComponent({
this.workflowsStore.removeNode(this.node);
this.ndvStore.activeNodeName = options.id ?? 'thisshouldnothappen';
this.workflowsStore.addNode(destinationToFakeINodeUi(options));
this.nodeParameters = options;
this.nodeParameters = options as INodeParameters;
this.logStreamingStore.items[this.destination.id].destination = options;
},
onTypeSelectInput(destinationType: MessageEventBusDestinationTypeNames) {
@@ -448,7 +451,7 @@ export default defineComponent({
if (deleteConfirmed !== MODAL_CONFIRM) {
return;
} else {
this.eventBus.emit('remove', this.destination.id);
this.callEventBus('remove', this.destination.id);
this.uiStore.closeModal(LOG_STREAM_MODAL_KEY);
this.uiStore.stateIsDirty = false;
}
@@ -456,10 +459,12 @@ export default defineComponent({
onModalClose() {
if (!this.hasOnceBeenSaved) {
this.workflowsStore.removeNode(this.node);
this.logStreamingStore.removeDestination(this.nodeParameters.id!);
if (this.nodeParameters.id) {
this.logStreamingStore.removeDestination(this.nodeParameters.id.toString());
}
}
this.ndvStore.activeNodeName = null;
this.eventBus.emit('closing', this.destination.id);
this.callEventBus('closing', this.destination.id);
this.uiStore.stateIsDirty = false;
},
async saveDestination() {
@@ -471,10 +476,12 @@ export default defineComponent({
this.hasOnceBeenSaved = true;
this.testMessageSent = false;
this.unchanged = true;
this.eventBus.emit('destinationWasSaved', this.destination.id);
this.callEventBus('destinationWasSaved', this.destination.id);
this.uiStore.stateIsDirty = false;
const destinationType = (this.nodeParameters.__type ?? 'unknown')
const destinationType = (
this.nodeParameters.__type ? `${this.nodeParameters.__type}` : 'unknown'
)
.replace('$$MessageEventBusDestination', '')
.toLowerCase();
@@ -503,6 +510,11 @@ export default defineComponent({
});
}
},
callEventBus(event: string, data: unknown) {
if (this.eventBus) {
this.eventBus.emit(event, data);
}
},
},
});
</script>

View File

@@ -184,7 +184,7 @@ export default defineComponent({
},
isSelected(): boolean {
return (
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data.name) !==
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data?.name) !==
undefined
);
},

View File

@@ -12,6 +12,8 @@ import { useUsersStore } from '@/stores/users.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { testingNodeTypes, mockNodeTypesToArray } from '@/__tests__/defaults';
import { setupServer } from '@/__tests__/server';
import { NodeConnectionType } from 'n8n-workflow';
import type { IConnections } from 'n8n-workflow';
const renderComponent = createComponentRenderer(WorkflowLMChatModal, {
props: {
@@ -23,25 +25,25 @@ const renderComponent = createComponentRenderer(WorkflowLMChatModal, {
async function createPiniaWithAINodes(options = { withConnections: true, withAgentNode: true }) {
const { withConnections, withAgentNode } = options;
const workflowId = uuid();
const connections: IConnections = {
'Chat Trigger': {
main: [
[
{
node: 'Agent',
type: NodeConnectionType.Main,
index: 0,
},
],
],
},
};
const workflow = createTestWorkflow({
id: workflowId,
name: 'Test Workflow',
connections: withConnections
? {
'Chat Trigger': {
main: [
[
{
node: 'Agent',
type: 'main',
index: 0,
},
],
],
},
}
: {},
active: true,
...(withConnections ? { connections } : {}),
nodes: [
createTestNode({
name: 'Chat Trigger',

View File

@@ -160,14 +160,14 @@ import { useRoute } from 'vue-router';
// eslint-disable-next-line unused-imports/no-unused-imports, @typescript-eslint/no-unused-vars
import type { BaseTextKey } from '@/plugins/i18n';
export interface IResource {
export type IResource = {
id: string;
name: string;
value: string;
updatedAt?: string;
createdAt?: string;
homeProject?: ProjectSharingData;
}
};
interface IFilters {
search: string;
@@ -291,11 +291,11 @@ export default defineComponent({
case 'lastUpdated':
return props.sortFns.lastUpdated
? props.sortFns.lastUpdated(a, b)
: new Date(b.updatedAt).valueOf() - new Date(a.updatedAt).valueOf();
: new Date(b.updatedAt ?? '').valueOf() - new Date(a.updatedAt ?? '').valueOf();
case 'lastCreated':
return props.sortFns.lastCreated
? props.sortFns.lastCreated(a, b)
: new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf();
: new Date(b.createdAt ?? '').valueOf() - new Date(a.createdAt ?? '').valueOf();
case 'nameAsc':
return props.sortFns.nameAsc
? props.sortFns.nameAsc(a, b)