Skip to content

Commit

Permalink
refactor: Fix type issues for parameter input components (#9449)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsmr committed May 21, 2024
1 parent cd751e7 commit 711c46f
Show file tree
Hide file tree
Showing 36 changed files with 317 additions and 245 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ export namespace n8n {
}
}

export type ExtendedValidationResult = Partial<ValidationResult> & { fieldName?: string };
export type ExtendedValidationResult = ValidationResult & { fieldName?: string };
48 changes: 25 additions & 23 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ import type {
WorkflowActivateMode,
WorkflowExecuteMode,
CallbackManager,
INodeParameters,
} from 'n8n-workflow';
import {
ExpressionError,
Expand Down Expand Up @@ -2121,13 +2122,12 @@ export function cleanupParameterData(inputData: NodeParameterValueType): void {
}

if (typeof inputData === 'object') {
type Key = keyof typeof inputData;
(Object.keys(inputData) as Key[]).forEach((key) => {
const value = inputData[key];
Object.keys(inputData).forEach((key) => {
const value = (inputData as INodeParameters)[key];
if (typeof value === 'object') {
if (DateTime.isDateTime(value)) {
// Is a special luxon date so convert to string
inputData[key] = value.toString();
(inputData as INodeParameters)[key] = value.toString();
} else {
cleanupParameterData(value);
}
Expand Down Expand Up @@ -2230,28 +2230,30 @@ const validateCollection = (
return validationResult;
}

for (const value of Array.isArray(validationResult.newValue)
? (validationResult.newValue as IDataObject[])
: [validationResult.newValue as IDataObject]) {
for (const key of Object.keys(value)) {
if (!validationMap[key]) continue;
if (validationResult.valid) {
for (const value of Array.isArray(validationResult.newValue)
? (validationResult.newValue as IDataObject[])
: [validationResult.newValue as IDataObject]) {
for (const key of Object.keys(value)) {
if (!validationMap[key]) continue;

const fieldValidationResult = validateFieldType(key, value[key], validationMap[key].type, {
valueOptions: validationMap[key].options,
});
const fieldValidationResult = validateFieldType(key, value[key], validationMap[key].type, {
valueOptions: validationMap[key].options,
});

if (!fieldValidationResult.valid) {
throw new ExpressionError(
`Invalid input for field '${validationMap[key].displayName}' inside '${propertyDescription.displayName}' in [item ${itemIndex}]`,
{
description: fieldValidationResult.errorMessage,
runIndex,
itemIndex,
nodeCause: node.name,
},
);
if (!fieldValidationResult.valid) {
throw new ExpressionError(
`Invalid input for field '${validationMap[key].displayName}' inside '${propertyDescription.displayName}' in [item ${itemIndex}]`,
{
description: fieldValidationResult.errorMessage,
runIndex,
itemIndex,
nodeCause: node.name,
},
);
}
value[key] = fieldValidationResult.newValue;
}
value[key] = fieldValidationResult.newValue;
}
}

Expand Down
12 changes: 7 additions & 5 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,17 @@ export type EndpointStyle = {
hoverMessage?: string;
};

export interface IUpdateInformation {
name: string;
key?: string;
value:
export interface IUpdateInformation<
T extends NodeParameterValueType =
| string
| number
| { [key: string]: string | number | boolean }
| NodeParameterValueType
| INodeParameters; // with null makes problems in NodeSettings.vue
| INodeParameters,
> {
name: string;
key?: string;
value: T;
node?: string;
oldValue?: string | number;
type?: 'optionsOrderChanged';
Expand Down
13 changes: 10 additions & 3 deletions packages/editor-ui/src/components/CodeNodeEditor/linter.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { defineComponent } from 'vue';
import type { Diagnostic } from '@codemirror/lint';
import { linter as createLinter } from '@codemirror/lint';
import type { EditorView } from '@codemirror/view';
import * as esprima from 'esprima-next';
import type { Node } from 'estree';
import type { CodeNodeEditorLanguage } from 'n8n-workflow';
import type { CodeExecutionMode, CodeNodeEditorLanguage } from 'n8n-workflow';
import { type PropType, defineComponent } from 'vue';

import {
DEFAULT_LINTER_DELAY_IN_MS,
DEFAULT_LINTER_SEVERITY,
OFFSET_FOR_SCRIPT_WRAPPER,
} from './constants';
import { walk } from './utils';
import type { RangeNode } from './types';
import { walk } from './utils';

export const linterExtension = defineComponent({
props: {
mode: {
type: String as PropType<CodeExecutionMode>,
required: true,
},
editor: { type: Object as PropType<EditorView | null>, default: null },
},
methods: {
createLinter(language: CodeNodeEditorLanguage) {
switch (language) {
Expand Down
2 changes: 1 addition & 1 deletion packages/editor-ui/src/components/CollectionParameter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export interface Props {
nodeValues: INodeParameters;
parameter: INodeProperties;
path: string;
values: INodeProperties;
values: INodeParameters;
isReadOnly?: boolean;
}
const emit = defineEmits<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ export default defineComponent({
name: 'ExpandableInputEdit',
components: { ExpandableInputBase },
props: {
modelValue: {},
placeholder: {},
maxlength: {},
autofocus: {},
modelValue: {
type: String,
required: true,
},
placeholder: { type: String, required: true },
maxlength: { type: Number },
autofocus: { type: Boolean },
eventBus: {
type: Object as PropType<EventBus>,
},
},
emits: ['update:modelValue', 'enter', 'blur', 'esc'],
mounted() {
// autofocus on input element is not reliable
if (this.autofocus && this.$refs.input) {
Expand Down
5 changes: 3 additions & 2 deletions packages/editor-ui/src/components/ExpressionEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { type PropType, defineComponent } from 'vue';
import { mapStores } from 'pinia';
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
import VariableSelector from '@/components/VariableSelector.vue';
Expand All @@ -98,6 +98,7 @@ import { useDebounce } from '@/composables/useDebounce';
import type { Segment } from '@/types/expressions';
import ExpressionOutput from './InlineExpressionEditor/ExpressionOutput.vue';
import { outputTheme } from './ExpressionEditorModal/theme';
import type { INodeProperties } from 'n8n-workflow';
export default defineComponent({
name: 'ExpressionEdit',
Expand All @@ -112,7 +113,7 @@ export default defineComponent({
default: false,
},
parameter: {
type: Object,
type: Object as PropType<INodeProperties>,
default: () => ({}),
},
path: {
Expand Down
11 changes: 6 additions & 5 deletions packages/editor-ui/src/components/ExpressionParameterInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ const inlineInput = ref<InstanceType<typeof InlineExpressionEditorInput>>();
type Props = {
path: string;
modelValue: string;
isReadOnly: boolean;
rows: number;
isAssignment: boolean;
additionalExpressionData: IDataObject;
eventBus: EventBus;
rows?: number;
additionalExpressionData?: IDataObject;
eventBus?: EventBus;
isReadOnly?: boolean;
isAssignment?: boolean;
};
const props = withDefaults(defineProps<Props>(), {
rows: 5,
isAssignment: false,
isReadOnly: false,
additionalExpressionData: () => ({}),
eventBus: () => createEventBus(),
});
Expand Down
13 changes: 7 additions & 6 deletions packages/editor-ui/src/components/FilterConditions/Condition.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import ParameterInputFull from '@/components/ParameterInputFull.vue';
import ParameterIssues from '@/components/ParameterIssues.vue';
import { useI18n } from '@/composables/useI18n';
import { DateTime } from 'luxon';
import {
type FilterConditionValue,
type FilterOptionsValue,
type INodeProperties,
import type {
FilterConditionValue,
FilterOptionsValue,
INodeProperties,
NodeParameterValue,
} from 'n8n-workflow';
import { computed, ref } from 'vue';
import OperatorSelect from './OperatorSelect.vue';
Expand Down Expand Up @@ -100,11 +101,11 @@ const rightParameter = computed<INodeProperties>(() => {
};
});
const onLeftValueChange = (update: IUpdateInformation): void => {
const onLeftValueChange = (update: IUpdateInformation<NodeParameterValue>): void => {
condition.value.leftValue = update.value;
};
const onRightValueChange = (update: IUpdateInformation): void => {
const onRightValueChange = (update: IUpdateInformation<NodeParameterValue>): void => {
condition.value.rightValue = update.value;
};
Expand Down
17 changes: 15 additions & 2 deletions packages/editor-ui/src/components/FilterConditions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,24 @@ import type { ConditionResult, FilterOperator } from './types';
export const getFilterOperator = (key: string) =>
OPERATORS_BY_ID[key as FilterOperatorId] as FilterOperator;

const convertToType = (value: unknown, type: FilterOperatorType): unknown => {
const getTargetType = (type: FilterOperatorType) => {
if (type === 'number') return 'number';
if (type === 'boolean') return 'boolean';
return 'string';
};

const convertToType = (value: NodeParameterValue, type: FilterOperatorType): NodeParameterValue => {
if (type === 'any') return value;

const fallback = type === 'boolean' ? false : value;

return validateFieldType('filter', value, type, { parseStrings: true }).newValue ?? fallback;
const validationResult = validateFieldType('filter', value, getTargetType(type), {
parseStrings: true,
});
if (!validationResult.valid) {
return fallback;
}
return validationResult.newValue ?? fallback;
};

export const handleOperatorChange = ({
Expand All @@ -42,6 +54,7 @@ export const handleOperatorChange = ({
if (leftTypeChanged && !isExpression(condition.leftValue)) {
condition.leftValue = convertToType(condition.leftValue, newOperator.type);
}

if (rightTypeChanged && !newOperator.singleValue && !isExpression(condition.rightValue)) {
condition.rightValue = convertToType(condition.rightValue, newRightType);
}
Expand Down
30 changes: 10 additions & 20 deletions packages/editor-ui/src/components/FixedCollectionParameter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@

<div v-if="parameterOptions.length > 0 && !isReadOnly" class="controls">
<n8n-button
v-if="parameter.options.length === 1"
v-if="parameter.options && parameter.options.length === 1"
type="tertiary"
block
:label="getPlaceholderText"
Expand Down Expand Up @@ -126,12 +126,7 @@ import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import type { IUpdateInformation } from '@/Interface';
import type {
INodeParameters,
INodeProperties,
INodePropertyCollection,
NodeParameterValue,
} from 'n8n-workflow';
import type { INodeParameters, INodeProperties, INodePropertyCollection } from 'n8n-workflow';
import { deepCopy, isINodePropertyCollectionList } from 'n8n-workflow';
import { get } from 'lodash-es';
Expand All @@ -140,7 +135,7 @@ export default defineComponent({
name: 'FixedCollectionParameter',
props: {
nodeValues: {
type: Object as PropType<Record<string, INodeParameters[]>>,
type: Object as PropType<INodeParameters>,
required: true,
},
parameter: {
Expand Down Expand Up @@ -297,23 +292,18 @@ export default defineComponent({
optionParameter.typeOptions.multipleValues === true
) {
// Multiple values are allowed so append option to array
newParameterValue[optionParameter.name] = get(
this.nodeValues,
[this.path, optionParameter.name],
[],
);
const multiValue = get(this.nodeValues, [this.path, optionParameter.name], []);
if (Array.isArray(optionParameter.default)) {
(newParameterValue[optionParameter.name] as INodeParameters[]).push(
...deepCopy(optionParameter.default as INodeParameters[]),
);
multiValue.push(...deepCopy(optionParameter.default));
} else if (
optionParameter.default !== '' &&
typeof optionParameter.default !== 'object'
) {
(newParameterValue[optionParameter.name] as NodeParameterValue[]).push(
deepCopy(optionParameter.default),
);
multiValue.push(deepCopy(optionParameter.default));
}
newParameterValue[optionParameter.name] = multiValue;
} else {
// Add a new option
newParameterValue[optionParameter.name] = deepCopy(optionParameter.default);
Expand All @@ -322,7 +312,7 @@ export default defineComponent({
let newValue;
if (this.multipleValues) {
newValue = get(this.nodeValues, name, [] as INodeParameters[]);
newValue = get(this.nodeValues, name, []) as INodeParameters[];
newValue.push(newParameterValue);
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/editor-ui/src/components/MultipleParameter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default defineComponent({
},
props: {
nodeValues: {
type: Object as PropType<Record<string, INodeParameters[]>>,
type: Object as PropType<INodeParameters>,
required: true,
},
parameter: {
Expand Down Expand Up @@ -159,7 +159,7 @@ export default defineComponent({
methods: {
addItem() {
const name = this.getPath();
const currentValue = get(this.nodeValues, name, [] as INodeParameters[]);
const currentValue = get(this.nodeValues, name, []) as INodeParameters[];
currentValue.push(deepCopy(this.parameter.default as INodeParameters));
Expand Down
7 changes: 6 additions & 1 deletion packages/editor-ui/src/components/NodeIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ export default defineComponent({
props: {
nodeType: {
type: Object as PropType<
INodeTypeDescription | IVersionNode | SimplifiedNodeType | ActionTypeDescription | null
| INodeTypeDescription
| IVersionNode
| SimplifiedNodeType
| ActionTypeDescription
| null
| undefined
>,
required: true,
},
Expand Down
Loading

0 comments on commit 711c46f

Please sign in to comment.