Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -474,15 +474,28 @@ export interface GetDataMapperCodedataResponse {
export interface PropertyRequest {
filePath: string;
codedata: CodeData;
propertyKey: string,
targetField: string;
}

export interface FieldPropertyRequest extends PropertyRequest {
fieldId: string;
}

export interface PropertyResponse {
property: Property;
}

export interface ClausePositionRequest {
filePath: string;
codedata: CodeData;
targetField: string;
index: number;
}

export interface ClausePositionResponse {
position: LinePosition;
}

export interface GraphqlDesignServiceParams {
filePath: string;
startLine: LinePosition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ import {
ProcessTypeReferenceResponse,
ProcessTypeReferenceRequest,
ExpandedDMModelResponse,
ClearTypeCacheResponse
ClearTypeCacheResponse,
FieldPropertyRequest,
ClausePositionRequest,
ClausePositionResponse
} from "../../interfaces/extended-lang-client";

export interface DataMapperAPI {
Expand All @@ -62,6 +65,8 @@ export interface DataMapperAPI {
getDataMapperCodedata: (params: GetDataMapperCodedataRequest) => Promise<GetDataMapperCodedataResponse>;
getSubMappingCodedata: (params: GetSubMappingCodedataRequest) => Promise<GetDataMapperCodedataResponse>;
getProperty: (params: PropertyRequest) => Promise<PropertyResponse>;
getFieldProperty: (params: FieldPropertyRequest) => Promise<PropertyResponse>;
getClausePosition: (params: ClausePositionRequest) => Promise<ClausePositionResponse>;
getExpandedDMFromDMModel: (params: DMModelRequest) => Promise<ExpandedDMModelResponse>;
getProcessTypeReference: (params: ProcessTypeReferenceRequest) => Promise<ProcessTypeReferenceResponse>;
clearTypeCache: () => Promise<ClearTypeCacheResponse>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ import {
ProcessTypeReferenceResponse,
ProcessTypeReferenceRequest,
ExpandedDMModelResponse,
ClearTypeCacheResponse
ClearTypeCacheResponse,
FieldPropertyRequest,
ClausePositionRequest,
ClausePositionResponse
} from "../../interfaces/extended-lang-client";
import { RequestType } from "vscode-messenger-common";

Expand All @@ -65,6 +68,8 @@ export const mapWithTransformFn: RequestType<MapWithFnRequest, DataMapperSourceR
export const getDataMapperCodedata: RequestType<GetDataMapperCodedataRequest, GetDataMapperCodedataResponse> = { method: `${_preFix}/getDataMapperCodedata` };
export const getSubMappingCodedata: RequestType<GetSubMappingCodedataRequest, GetDataMapperCodedataResponse> = { method: `${_preFix}/getSubMappingCodedata` };
export const getProperty: RequestType<PropertyRequest, PropertyResponse> = { method: `${_preFix}/getProperty` };
export const getFieldProperty: RequestType<FieldPropertyRequest, PropertyResponse> = { method: `${_preFix}/getFieldProperty` };
export const getClausePosition: RequestType<ClausePositionRequest, ClausePositionResponse> = { method: `${_preFix}/getClausePosition` };
export const getExpandedDMFromDMModel: RequestType<DMModelRequest, ExpandedDMModelResponse> = { method: `${_preFix}/getExpandedDMFromDMModel` };
export const getProcessTypeReference: RequestType<ProcessTypeReferenceRequest, ProcessTypeReferenceResponse> = { method: `${_preFix}/getProcessTypeReference` };
export const clearTypeCache: RequestType<void, ClearTypeCacheResponse> = { method: `${_preFix}/clearTypeCache` };
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,10 @@ import {
ProjectInfoRequest,
ProjectInfo,
onMigratedProject,
ProjectMigrationResult
ProjectMigrationResult,
FieldPropertyRequest,
ClausePositionResponse,
ClausePositionRequest
} from "@wso2/ballerina-core";
import { BallerinaExtension } from "./index";
import { debug, handlePullModuleProgress } from "../utils";
Expand Down Expand Up @@ -371,6 +374,8 @@ enum EXTENDED_APIS {
DATA_MAPPER_CODEDATA = 'dataMapper/nodePosition',
DATA_MAPPER_SUB_MAPPING_CODEDATA = 'dataMapper/subMapping',
DATA_MAPPER_PROPERTY = 'dataMapper/targetFieldPosition',
DATA_MAPPER_FIELD_PROPERTY = 'dataMapper/fieldPosition',
DATA_MAPPER_CLAUSE_POSITION = 'dataMapper/clausePosition',
DATA_MAPPER_CLEAR_TYPE_CACHE = 'dataMapper/clearTypeCache',
VIEW_CONFIG_VARIABLES = 'configEditor/getConfigVariables',
UPDATE_CONFIG_VARIABLES = 'configEditor/updateConfigVariables',
Expand Down Expand Up @@ -845,6 +850,14 @@ export class ExtendedLangClient extends LanguageClient implements ExtendedLangCl
return this.sendRequest<PropertyResponse>(EXTENDED_APIS.DATA_MAPPER_PROPERTY, params);
}

async getFieldProperty(params: FieldPropertyRequest): Promise<PropertyResponse | NOT_SUPPORTED_TYPE> {
return this.sendRequest<PropertyResponse>(EXTENDED_APIS.DATA_MAPPER_FIELD_PROPERTY, params);
}

async getClausePosition(params: ClausePositionRequest): Promise<ClausePositionResponse> {
return this.sendRequest<ClausePositionResponse>(EXTENDED_APIS.DATA_MAPPER_CLAUSE_POSITION, params);
}

async clearTypeCache(): Promise<ClearTypeCacheResponse> {
return this.sendRequest<ClearTypeCacheResponse>(EXTENDED_APIS.DATA_MAPPER_CLEAR_TYPE_CACHE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
addNewArrayElement,
addSubMapping,
AddSubMappingRequest,
ClausePositionRequest,
clearTypeCache,
convertToQuery,
ConvertToQueryRequest,
Expand All @@ -36,11 +37,14 @@ import {
deleteSubMapping,
DeleteSubMappingRequest,
DMModelRequest,
FieldPropertyRequest,
getClausePosition,
getDataMapperCodedata,
GetDataMapperCodedataRequest,
getDataMapperModel,
getDataMapperSource,
getExpandedDMFromDMModel,
getFieldProperty,
getInitialIDMSource,
getProcessTypeReference,
getProperty,
Expand All @@ -53,7 +57,7 @@ import {
mapWithTransformFn,
ProcessTypeReferenceRequest,
PropertyRequest,
VisualizableFieldsRequest
VisualizableFieldsRequest,
} from "@wso2/ballerina-core";
import { Messenger } from "vscode-messenger";
import { DataMapperRpcManager } from "./rpc-manager";
Expand All @@ -76,6 +80,8 @@ export function registerDataMapperRpcHandlers(messenger: Messenger) {
messenger.onRequest(getDataMapperCodedata, (args: GetDataMapperCodedataRequest) => rpcManger.getDataMapperCodedata(args));
messenger.onRequest(getSubMappingCodedata, (args: GetSubMappingCodedataRequest) => rpcManger.getSubMappingCodedata(args));
messenger.onRequest(getProperty, (args: PropertyRequest) => rpcManger.getProperty(args));
messenger.onRequest(getFieldProperty, (args: FieldPropertyRequest) => rpcManger.getFieldProperty(args));
messenger.onRequest(getClausePosition, (args: ClausePositionRequest) => rpcManger.getClausePosition(args));
messenger.onRequest(getExpandedDMFromDMModel, (args: DMModelRequest) => rpcManger.getExpandedDMFromDMModel(args));
messenger.onRequest(getProcessTypeReference, (args: ProcessTypeReferenceRequest) => rpcManger.getProcessTypeReference(args));
messenger.onRequest(clearTypeCache, () => rpcManger.clearTypeCache());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
AddArrayElementRequest,
AddClausesRequest,
AddSubMappingRequest,
ClausePositionRequest,
ClausePositionResponse,
ClearTypeCacheResponse,
ConvertToQueryRequest,
DataMapperAPI,
Expand All @@ -34,6 +36,7 @@ import {
DMModelRequest,
ExpandedDMModel,
ExpandedDMModelResponse,
FieldPropertyRequest,
GetDataMapperCodedataRequest,
GetDataMapperCodedataResponse,
GetSubMappingCodedataRequest,
Expand Down Expand Up @@ -233,6 +236,26 @@ export class DataMapperRpcManager implements DataMapperAPI {
});
}

async getFieldProperty(params: FieldPropertyRequest): Promise<PropertyResponse> {
return new Promise(async (resolve) => {
const property = await StateMachine
.langClient()
.getFieldProperty(params) as PropertyResponse;

resolve(property);
});
}

async getClausePosition(params: ClausePositionRequest): Promise<ClausePositionResponse> {
return new Promise(async (resolve) => {
const position: any = await StateMachine
.langClient()
.getClausePosition(params);

resolve(position);
});
}

async deleteMapping(params: DeleteMappingRequest): Promise<DataMapperSourceResponse> {
return new Promise(async (resolve) => {
await StateMachine
Expand Down Expand Up @@ -394,4 +417,5 @@ export class DataMapperRpcManager implements DataMapperAPI {
});
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
AddArrayElementRequest,
AddClausesRequest,
AddSubMappingRequest,
AllDataMapperSourceRequest,
ClausePositionRequest,
ClausePositionResponse,
ClearTypeCacheResponse,
ConvertToQueryRequest,
DMModelRequest,
Expand All @@ -34,6 +35,7 @@ import {
DeleteMappingRequest,
DeleteSubMappingRequest,
ExpandedDMModelResponse,
FieldPropertyRequest,
GetDataMapperCodedataRequest,
GetDataMapperCodedataResponse,
GetSubMappingCodedataRequest,
Expand All @@ -54,10 +56,12 @@ import {
deleteClause,
deleteMapping,
deleteSubMapping,
getClausePosition,
getDataMapperCodedata,
getDataMapperModel,
getDataMapperSource,
getExpandedDMFromDMModel,
getFieldProperty,
getInitialIDMSource,
getProcessTypeReference,
getProperty,
Expand Down Expand Up @@ -140,6 +144,14 @@ export class DataMapperRpcClient implements DataMapperAPI {
return this._messenger.sendRequest(getProperty, HOST_EXTENSION, params);
}

getFieldProperty(params: FieldPropertyRequest): Promise<PropertyResponse> {
return this._messenger.sendRequest(getFieldProperty, HOST_EXTENSION, params);
}

getClausePosition(params: ClausePositionRequest): Promise<ClausePositionResponse> {
return this._messenger.sendRequest(getClausePosition, HOST_EXTENSION, params);
}

getExpandedDMFromDMModel(params: DMModelRequest): Promise<ExpandedDMModelResponse> {
return this._messenger.sendRequest(getExpandedDMFromDMModel, HOST_EXTENSION, params);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { FormField } from "../Form/types";
import { MultiSelectEditor } from "./MultiSelectEditor";
import { TextEditor } from "./TextEditor";
import { TypeEditor } from "./TypeEditor";
import { ContextAwareExpressionEditor } from "./ExpressionEditor";
import { ContextAwareExpressionEditor, DataMapperJoinClauseRhsEditor } from "./ExpressionEditor";
import { ParamManagerEditor } from "../ParamManager/ParamManager";
import { DropdownEditor } from "./DropdownEditor";
import { FileSelect } from "./FileSelect";
Expand Down Expand Up @@ -218,6 +218,19 @@ export const EditorFactory = (props: FormFieldEditorProps) => {
field={field}
/>
);
} else if (field.type === "DM_JOIN_CLAUSE_RHS_EXPRESSION") {
// Expression field for Data Mapper join on condition RHS
return (
<DataMapperJoinClauseRhsEditor
field={field}
openSubPanel={openSubPanel}
subPanelView={subPanelView}
handleOnFieldFocus={handleOnFieldFocus}
onBlur={onBlur}
autoFocus={autoFocus}
recordTypeField={recordTypeFields?.find(recordField => recordField.key === field.key)}
/>
);
} else {
// Default to text editor
// Readonly fields are also treated as text editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { getPropertyFromFormField, sanitizeType } from './utils';
import { FormField, FormExpressionEditorProps, HelperpaneOnChangeOptions } from '../Form/types';
import { useFormContext } from '../../context';
import {
ExpressionProperty,
LineRange,
RecordTypeField,
SubPanel,
Expand Down Expand Up @@ -303,6 +304,33 @@ export const ContextAwareExpressionEditor = (props: ContextAwareExpressionEditor
);
};

export const DataMapperJoinClauseRhsEditor = (props: ContextAwareExpressionEditorProps) => {
const { form, expressionEditor, targetLineRange, fileName } = useFormContext();

const modifiedExpressionEditor = {
...expressionEditor
};

modifiedExpressionEditor.retrieveCompletions = async (value: string, property: ExpressionProperty, offset: number, triggerCharacter?: string) => {
const varName = form.watch('name');
const expression = form.watch('expression');
const prefixExpr = `from var ${varName} in ${expression} select `;
return await expressionEditor.retrieveCompletions(prefixExpr + value, property, prefixExpr.length + offset, triggerCharacter);
}

return (
<ExpressionEditor
fileName={fileName}
targetLineRange={targetLineRange}
helperPaneZIndex={props.helperPaneZIndex}
{...form}
{...modifiedExpressionEditor}
{...props}
/>
);
};


export const ExpressionEditor = (props: ExpressionEditorProps) => {
const {
autoFocus,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ export function DataMapperView(props: DataMapperProps) {
fileName={filePath}
preserveFieldOrder={true}
helperPaneSide="left"
isDataMapperEditor={true}
{...formProps}
targetLineRange={viewState.codedata.lineRange}
/>
)
}
Expand Down Expand Up @@ -360,6 +360,20 @@ export function DataMapperView(props: DataMapperProps) {
}
}

const getClausePosition = async (targetField: string, index: number) => {
try {
const { position } = await rpcClient.getDataMapperRpcClient().getClausePosition({
filePath,
codedata: viewState.codedata,
targetField: targetField,
index: index
});
return position;
} catch (error) {
console.error(error);
}
}
Comment on lines +363 to +375
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Improve error handling and add explicit return type.

The function silently swallows errors and returns undefined implicitly, making it difficult for callers to distinguish between "position not found" and "error occurred" scenarios.

Apply this diff:

-    const getClausePosition = async (targetField: string, index: number) => {
+    const getClausePosition = async (targetField: string, index: number): Promise<NodePosition | undefined> => {
         try {
             const { position } = await rpcClient.getDataMapperRpcClient().getClausePosition({
                 filePath,
                 codedata: viewState.codedata,
-                targetField: targetField,
-                index: index
+                targetField,
+                index
             });
             return position;
         } catch (error) {
             console.error(error);
+            return undefined;
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getClausePosition = async (targetField: string, index: number) => {
try {
const { position } = await rpcClient.getDataMapperRpcClient().getClausePosition({
filePath,
codedata: viewState.codedata,
targetField: targetField,
index: index
});
return position;
} catch (error) {
console.error(error);
}
}
const getClausePosition = async (targetField: string, index: number): Promise<NodePosition | undefined> => {
try {
const { position } = await rpcClient.getDataMapperRpcClient().getClausePosition({
filePath,
codedata: viewState.codedata,
targetField,
index
});
return position;
} catch (error) {
console.error(error);
return undefined;
}
}
🤖 Prompt for AI Agents
In
workspaces/ballerina/ballerina-visualizer/src/views/DataMapper/DataMapperView.tsx
around lines 363-375, the async getClausePosition swallows errors and returns
undefined implicitly; change its signature to an explicit return type (e.g.,
Promise<number | null>), and in the catch block log the error and re-throw it
(or reject) instead of returning/ignoring, so callers can distinguish a real
error from a "not found" null/number result; update any callers to handle the
thrown error or the explicit null return as needed.


const addSubMapping = async (
subMappingName: string,
type: string,
Expand Down Expand Up @@ -492,13 +506,12 @@ export function DataMapperView(props: DataMapperProps) {
};

const goToSource = async (outputId: string, viewId: string) => {
const { property } = await rpcClient.getDataMapperRpcClient().getProperty({
const { property } = await rpcClient.getDataMapperRpcClient().getFieldProperty({
filePath,
codedata: viewState.codedata,
propertyKey: "expression", // TODO: Remove this once the API is updated
targetField: viewId,
fieldId: outputId,
})
});
if (property.codedata) {
const position: NodePosition = {
startLine: property.codedata.lineRange?.startLine?.line,
Expand Down Expand Up @@ -593,9 +606,7 @@ export function DataMapperView(props: DataMapperProps) {
const { property } = await rpcClient.getDataMapperRpcClient().getProperty({
filePath,
codedata: viewState.codedata,
propertyKey: "expression", // TODO: Remove this once the API is updated
targetField: viewId,
fieldId: outputId,
targetField: viewId
})
const { lineOffset, charOffset } = calculateExpressionOffsets(value, cursorPosition);
const startLine = updateLineRange(property.codedata.lineRange, expressionOffsetRef.current).startLine;
Expand Down Expand Up @@ -689,6 +700,7 @@ export function DataMapperView(props: DataMapperProps) {
convertToQuery={convertToQuery}
addClauses={addClauses}
deleteClause={deleteClause}
getClausePosition={getClausePosition}
addSubMapping={addSubMapping}
deleteMapping={deleteMapping}
deleteSubMapping={deleteSubMapping}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export function DataMapperEditor(props: DataMapperEditorProps) {
generateForm,
addClauses,
deleteClause,
getClausePosition,
mapWithCustomFn,
mapWithTransformFn,
goToFunction,
Expand Down Expand Up @@ -361,6 +362,7 @@ export function DataMapperEditor(props: DataMapperEditorProps) {
targetField={views[views.length - 1].targetField}
addClauses={addClauses}
deleteClause={deleteClause}
getClausePosition={getClausePosition}
generateForm={generateForm}
/>
)}
Expand Down
Loading
Loading