Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export enum TypeKind {
Boolean = "boolean",
Enum = "enum",
Union = "union",
Tuple = "tuple",
Unknown = "$CompilationError$",
Anydata = "anydata",
Byte = "byte",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,14 @@ function processTypeKind(
}
break;

case TypeKind.Tuple:
if (type.members) {
return {
members: processTuple(type.members, parentId, model, visitedRefs)
};
}
break;

case TypeKind.Record:
if (type.ref) {
return processTypeReference(type.ref, parentId, model, visitedRefs);
Expand Down Expand Up @@ -783,3 +791,35 @@ function processEnum(
}));
}

/**
* Processes tuple type members and returns an array of IOType objects
* Each tuple member is processed with its index-based identifier
*/
function processTuple(
tupleMembers: IOTypeField[],
parentId: string,
model: DMModel,
visitedRefs: Set<string>
): IOType[] {
return tupleMembers.map((tupleMember, index) => {
const memberFieldId = `${parentId}[${index}]`;

const tupleMemberType: IOType = {
id: memberFieldId,
name: tupleMember.name,
Comment thread
KCSAbeywickrama marked this conversation as resolved.
displayName: tupleMember.displayName,
typeName: tupleMember.typeName,
kind: tupleMember.kind,
...(tupleMember.optional && { optional: tupleMember.optional }),
...(tupleMember.typeInfo && { typeInfo: tupleMember.typeInfo })
};

const typeSpecificProps = processTypeKind(tupleMember, memberFieldId, model, visitedRefs);

return {
...tupleMemberType,
...typeSpecificProps
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
}

Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ export class InputNode extends DataMapperNodeModel {
focusedFieldFQNs
});
}
} else if (this.filteredInputType.kind === TypeKind.Tuple) {
const members = this.filteredInputType.members?.filter(m => !!m);
for (const member of members) {
this.numberOfFields += await this.addPortsForInputField({
field: member,
portType: "OUT",
parentId: this.identifier,
unsafeParentId: this.identifier,
parent: parentPort,
collapsedFields,
expandedFields,
hidden: parentPort.attributes.collapsed,
isOptional: member.optional,
focusedFieldFQNs
});
}
} else if (this.filteredInputType.kind === TypeKind.Enum) {
for (const member of this.filteredInputType.members ?? []) {
this.numberOfFields += await this.addPortsForInputField({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class InputNodeFactory extends AbstractReactFactory<InputNode, DiagramEng
);
} else if (event.model.filteredInputType &&
(event.model.filteredInputType.kind === TypeKind.Record ||
event.model.filteredInputType.kind === TypeKind.Tuple ||
event.model.filteredInputType.kind === TypeKind.Array ||
event.model.filteredInputType.kind === TypeKind.Enum
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,18 @@ export function InputNodeTreeItemWidget(props: InputNodeTreeItemWidgetProps) {
const fieldName = dmType.name;
const displayName = dmType.displayName || fieldName;
const typeName = getTypeName(dmType);
const fieldId = dmType.isFocused ? fieldName : `${parentId}.${fieldName}`;

// For tuple members, use the dmType.id directly if it contains bracket notation
// This ensures we match the port names created by the backend (e.g., "tupleVar[0]" not "tupleVar.0")
let fieldId: string;
if (dmType.id && dmType.id.includes('[') && dmType.id.includes(']')) {
fieldId = dmType.id;
} else if (dmType.isFocused) {
fieldId = fieldName;
} else {
fieldId = `${parentId}.${fieldName}`;
}

const portOut = getPort(`${fieldId}.OUT`);
const isUnknownType = dmType.kind === TypeKind.Unknown;

Expand All @@ -61,6 +72,8 @@ export function InputNodeTreeItemWidget(props: InputNodeTreeItemWidgetProps) {

if (dmType.kind === TypeKind.Record) {
fields = dmType.fields;
} else if (dmType.kind === TypeKind.Tuple) {
fields = dmType.members;
} else if (dmType.kind === TypeKind.Array) {
fields = [ dmType.member ];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export function InputNodeWidget(props: InputNodeWidgetProps) {

if (dmType.kind === TypeKind.Record) {
fields = dmType.fields;
} else if (dmType.kind === TypeKind.Tuple) {
fields = dmType.members;
} else if (dmType.kind === TypeKind.Array) {
fields = [ dmType.member ];
} else if (dmType.kind === TypeKind.Enum) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,42 @@ export function ObjectOutputFieldWidget(props: ObjectOutputFieldWidgetProps) {

const isArray = typeKind === TypeKind.Array;
const isRecord = typeKind === TypeKind.Record;
const isTuple = typeKind === TypeKind.Tuple;
const isEnum = typeKind === TypeKind.Enum;

let updatedParentId = parentId;

if (fieldIndex !== undefined) {
updatedParentId = `${updatedParentId}.${fieldIndex}`
}

let fieldName = field?.name || '';
let displayName = field?.displayName || fieldName;

let portName = isPortParent
? parentId
: updatedParentId !== ''
? fieldName !== '' && fieldIndex === undefined
? `${updatedParentId}.${fieldName}`
: updatedParentId
: fieldName;
// For tuple members, we need to construct the port name with the prefix
// Backend creates field.id with full path like "updatedPerson.data[0]"
// Ports are named with prefix: "objectOutput.updatedPerson.data[0]"
let portName: string;
if (field?.id && field.id.includes('[') && field.id.includes(']')) {
// Extract the port prefix (e.g., "objectOutput" from "objectOutput.updatedPerson.data")
// The prefix is the part before the first dot
const firstDotIndex = parentId.indexOf('.');
if (firstDotIndex >= 0) {
const prefix = parentId.substring(0, firstDotIndex);
// field.id already contains the full path (e.g., "updatedPerson.data[0]")
portName = `${prefix}.${field.id}`;
} else {
portName = field.id;
}
} else if (isPortParent) {
portName = parentId;
} else if (updatedParentId !== '') {
portName = fieldName !== '' && fieldIndex === undefined
? `${updatedParentId}.${fieldName}`
: updatedParentId;
} else {
portName = fieldName;
}

const portIn = getPort(portName + ".IN");
const isUnknownType = field?.kind === TypeKind.Unknown;
Expand All @@ -110,6 +128,7 @@ export function ObjectOutputFieldWidget(props: ObjectOutputFieldWidgetProps) {
!isEnum;

const fields = isRecord && field?.fields?.filter(f => f !== null);
const members = isTuple && field?.members?.filter(m => m !== null);
const isWithinArray = fieldIndex !== undefined;

const handleExpand = () => {
Expand Down Expand Up @@ -201,7 +220,7 @@ export function ObjectOutputFieldWidget(props: ObjectOutputFieldWidgetProps) {
className={classnames(classes.valueLabel,
isDisabled && !hasHoveredParent ? classes.labelDisabled : ""
)}
style={{ marginLeft: fields ? 0 : indentation + 24 }}
style={{ marginLeft: (fields || members) ? 0 : indentation + 24 }}
>
<OutputSearchHighlight>{displayName}</OutputSearchHighlight>
{!field?.optional && <span className={classes.requiredMark}>*</span>}
Expand Down Expand Up @@ -301,9 +320,9 @@ export function ObjectOutputFieldWidget(props: ObjectOutputFieldWidgetProps) {
)}
</span>
<span className={classes.label}>
{fields && (
{(fields || members) && (
<Button
id={"expand-or-collapse-" + portName}
id={"expand-or-collapse-" + portName}
appearance="icon"
tooltip="Expand/Collapse"
sx={{ marginLeft: indentation }}
Expand Down Expand Up @@ -356,6 +375,22 @@ export function ObjectOutputFieldWidget(props: ObjectOutputFieldWidgetProps) {
);
})
}
{members && expanded &&
members.map((member, index) => {
return (
<ObjectOutputFieldWidget
key={index}
engine={engine}
field={member}
getPort={getPort}
parentId={portName}
context={context}
treeDepth={treeDepth + 1}
hasHoveredParent={isHovered || hasHoveredParent}
/>
);
})
}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ export class ObjectOutputNode extends DataMapperNodeModel {
}
}
}

if (this.filteredOutputType.kind === TypeKind.Tuple) {
if (this.filteredOutputType.members && this.filteredOutputType.members.length > 0) {
for (const member of this.filteredOutputType.members) {
if (!member) continue;
await this.addPortsForOutputField({
field: member,
type: "IN",
parentId: this.rootName,
mappings: this.context.model.mappings,
portPrefix: OBJECT_OUTPUT_TARGET_PORT_PREFIX,
parent: parentPort,
collapsedFields,
expandedFields,
hidden: parentPort.attributes.collapsed
});
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ export function ObjectOutputWidget(props: ObjectOutputWidgetProps) {
}))
);

const fields = outputType.fields.filter(t => t !== null);
// Handle both records (fields) and tuples (members)
const isTuple = outputType.kind === TypeKind.Tuple;
const fields = isTuple
? (outputType.members?.filter(t => t !== null) || [])
: (outputType.fields?.filter(t => t !== null) || []);
const hasFields = fields.length > 0;

const portIn = getPort(`${id}.IN`);
Expand Down
Loading
Loading