Skip to content
Merged
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.ballerina.flowmodelgenerator.extension.request.DataMapperClauseDiagnosticsRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperClausePositionRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperConvertRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperConvertTypeRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperCustomFunctionRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperDeleteClauseRequest;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperDeleteSubMappingRequest;
Expand Down Expand Up @@ -573,6 +574,28 @@ public CompletableFuture<DataMapperConvertResponse> convertExpression(DataMapper
});
}

@JsonRequest
public CompletableFuture<DataMapperSourceResponse> convertType(DataMapperConvertTypeRequest request) {
return CompletableFuture.supplyAsync(() -> {
DataMapperSourceResponse response = new DataMapperSourceResponse();
try {
Path filePath = Path.of(request.filePath());
this.workspaceManager.loadProject(filePath);
Optional<Document> document = this.workspaceManager.document(filePath);
Optional<SemanticModel> semanticModel = this.workspaceManager.semanticModel(filePath);
if (document.isEmpty() || semanticModel.isEmpty()) {
return response;
}
DataMapManager dataMapManager = new DataMapManager(document.get());
response.setTextEdits(dataMapManager.convertType(filePath, semanticModel.get(), request.codedata(),
request.typeName(), request.variableName(), request.isInput()));
} catch (Throwable e) {
response.setError(e);
}
return response;
});
}

private Optional<Document> getDocumentFromFile(Path projectPath, String fileName) {
try {
return this.workspaceManagerProxy.get().document(projectPath.resolve(fileName));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com)
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package io.ballerina.flowmodelgenerator.extension.request;

import com.google.gson.JsonElement;

/**
* Represents a request to convert an expression from one type to another for incompatible types.
*
* @param filePath The file path of the source file
* @param codedata The details of the node
* @param typeName The converted type name
* @param variableName The converted variable name
* @param isInput The converted variable is input or output
*
* @since 2.0.0
*/
public record DataMapperConvertTypeRequest(String filePath, JsonElement codedata, String typeName,
String variableName, boolean isInput) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2026, WSO2 LLC. (http://www.wso2.com)
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package io.ballerina.flowmodelgenerator.extension;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import io.ballerina.flowmodelgenerator.extension.request.DataMapperConvertTypeRequest;
import io.ballerina.modelgenerator.commons.AbstractLSTest;
import org.eclipse.lsp4j.TextEdit;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Tests for the generation of type conversion in data mapper.
*
* @since 2.0.0
*/
public class DataMappingConvertTypeTest extends AbstractLSTest {

private static final Type textEditListType = new TypeToken<Map<String, List<TextEdit>>>() {
}.getType();

@DataProvider(name = "data-provider")
@Override
protected Object[] getConfigsList() {
return new Object[][]{
{Path.of("variable1.json")},
{Path.of("variable2.json")},
{Path.of("variable3.json")},
{Path.of("variable4.json")},
{Path.of("variable5.json")},
{Path.of("variable6.json")},
{Path.of("variable7.json")},
{Path.of("variable8.json")},
{Path.of("variable9.json")},
{Path.of("variable10.json")},
{Path.of("variable11.json")},
{Path.of("variable12.json")},
};
}

@Override
@Test(dataProvider = "data-provider")
public void test(Path config) throws IOException {
Path configJsonPath = configDir.resolve(config);
TestConfig testConfig = gson.fromJson(Files.newBufferedReader(configJsonPath), TestConfig.class);

DataMapperConvertTypeRequest request =
new DataMapperConvertTypeRequest(sourceDir.resolve(testConfig.source()).toAbsolutePath().toString(),
testConfig.codedata(), testConfig.typeName(), testConfig.variableName(),
testConfig.isInput());
JsonObject jsonMap =
getResponseAndCloseFile(request, testConfig.source()).getAsJsonObject("textEdits");

Map<String, List<TextEdit>> actualTextEdits = gson.fromJson(jsonMap, textEditListType);

boolean assertFailure = false;

if (actualTextEdits.size() != testConfig.output().size()) {
log.info("The number of text edits does not match the expected output.");
assertFailure = true;
}

Map<String, List<TextEdit>> newMap = new HashMap<>();
for (Map.Entry<String, List<TextEdit>> entry : actualTextEdits.entrySet()) {
Path fullPath = Paths.get(entry.getKey());
String relativePath = sourceDir.relativize(fullPath).toString();

List<TextEdit> textEdits = testConfig.output().get(relativePath.replace("\\", "/"));
if (textEdits == null) {
log.info("No text edits found for the file: " + relativePath);
assertFailure = true;
} else if (!assertArray("text edits", entry.getValue(), textEdits)) {
assertFailure = true;
}

newMap.put(relativePath, entry.getValue());
}

if (assertFailure) {
TestConfig updatedConfig = new TestConfig(testConfig.source(), testConfig.description(),
testConfig.codedata(), testConfig.typeName(), testConfig.variableName(), testConfig.isInput(),
newMap);
// updateConfig(configJsonPath, updatedConfig);
Assert.fail(String.format("Failed test: '%s' (%s)", testConfig.description(), configJsonPath));
}
}

@Override
protected String getResourceDir() {
return "data_mapper_convert_type";
}

@Override
protected Class<? extends AbstractLSTest> clazz() {
return DataMappingConvertTypeTest.class;
}

@Override
protected String getApiName() {
return "convertType";
}

@Override
protected String getServiceName() {
return "dataMapper";
}

/**
* Represents the test configuration for the type conversion tests.
*
* @param source The source file name
* @param description The description of the test
* @param codedata The Details of the node
* @param typeName The name of the converted type
* @param variableName The name of the converting variable
* @param isInput Whether the variable is an input variable or not
* @param output Generated source expression
*/
private record TestConfig(String source, String description, JsonElement codedata,
String typeName, String variableName, boolean isInput,
Map<String, List<TextEdit>> output) {

public String description() {
return description == null ? "" : description;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ protected Object[] getConfigsList() {
{Path.of("query29.json")},
{Path.of("variable58.json")},
{Path.of("variable59.json")},
{Path.of("function_def_transformed_type1.json")},
{Path.of("function_def_transformed_type2.json")},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ protected Object[] getConfigsList() {
{Path.of("query7.json")},
{Path.of("query8.json")},
{Path.of("variable20.json")},
{Path.of("function_defn4.json")},
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"source": "variable1.bal",
"description": "Sample diagram node",
"codedata": {
"node": "VARIABLE",
"lineRange": {
"fileName": "main.bal",
"startLine": {
"line": 14,
"offset": 0
},
"endLine": {
"line": 16,
"offset": 2
}
},
"isNew": true,
"sourceCode": ""
},
"typeName": "UserInfo",
"variableName": "user",
"isInput": true,
"output": {
"variable1.bal": [
{
"range": {
"start": {
"line": 14,
"character": 67
},
"end": {
"line": 16,
"character": 1
}
},
"newText": "let UserInfo userConverted = check user.ensureType() in {\n\n}"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"source": "variable1.bal",
"description": "Sample diagram node",
"codedata": {
"node": "VARIABLE",
"lineRange": {
"fileName": "main.bal",
"startLine": {
"line": 33,
"offset": 0
},
"endLine": {
"line": 35,
"offset": 2
}
},
"isNew": true,
"sourceCode": ""
},
"typeName": "UserInfo",
"variableName": "user",
"isInput": true,
"output": {
"variable1.bal": [
{
"range": {
"start": {
"line": 33,
"character": 67
},
"end": {
"line": 35,
"character": 1
}
},
"newText": "let UserInfo userConverted = check user.ensureType() in {\n\n}"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"source": "variable1.bal",
"description": "Sample diagram node",
"codedata": {
"node": "VARIABLE",
"lineRange": {
"fileName": "main.bal",
"startLine": {
"line": 37,
"offset": 0
},
"endLine": {
"line": 39,
"offset": 2
}
},
"isNew": true,
"sourceCode": ""
},
"typeName": "UserInfo",
"variableName": "user",
"isInput": true,
"output": {
"variable1.bal": [
{
"range": {
"start": {
"line": 37,
"character": 44
},
"end": {
"line": 37,
"character": 44
}
},
"newText": " returns error?"
},
{
"range": {
"start": {
"line": 37,
"character": 48
},
"end": {
"line": 39,
"character": 1
}
},
"newText": "let UserInfo userConverted = check user.ensureType() in {\n\n}"
}
]
}
}
Loading
Loading