Skip to content

Commit

Permalink
Merge pull request #426 from KavinduZoysa/configurables
Browse files Browse the repository at this point in the history
Add API to get config variables
  • Loading branch information
KavinduZoysa authored Oct 10, 2024
2 parents 63e8232 + 81ef342 commit a2ecb9c
Show file tree
Hide file tree
Showing 13 changed files with 698 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2024, 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.core;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import io.ballerina.compiler.api.symbols.Qualifier;
import io.ballerina.compiler.syntax.tree.ExpressionNode;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.compiler.syntax.tree.SyntaxTree;
import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode;
import io.ballerina.flowmodelgenerator.core.model.Codedata;
import io.ballerina.flowmodelgenerator.core.model.Metadata;
import io.ballerina.flowmodelgenerator.core.model.NodeKind;
import io.ballerina.flowmodelgenerator.core.model.Property;
import io.ballerina.projects.Document;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Manage the configurable variables.
*
* @since 1.4.0
*/
public class ConfigVariablesManager {

private final Gson gson;
public static final String CONFIG_TYPE = "Config type";
public static final String CONFIG_TYPE_DESCRIPTION = "Type of the configuration";
public static final String CONFIG_NAME = "Config name";
public static final String CONFIG_NAME_DESCRIPTION = "Name of the config variable";
public static final String DEFAULT_VALUE = "Default value";
public static final String DEFAULT_VALUE_DESCRIPTION = "Default value for the config, if empty your need to " +
"provide a value at runtime";

public ConfigVariablesManager() {
this.gson = new Gson();
}

public JsonElement get(Document document) {
SyntaxTree syntaxTree = document.syntaxTree();
ModulePartNode modulePartNode = syntaxTree.rootNode();
List<ConfigVariable> configVariables = new ArrayList<>();
for (Node node : modulePartNode.children()) {
if (node.kind() == SyntaxKind.MODULE_VAR_DECL) {
ModuleVariableDeclarationNode modVarDeclarationNode = (ModuleVariableDeclarationNode) node;
if (hasConfigurableQualifier(modVarDeclarationNode)) {
configVariables.add(genConfigVariable(modVarDeclarationNode));
}
}
}
return gson.toJsonTree(configVariables);
}

private static boolean hasConfigurableQualifier(ModuleVariableDeclarationNode modVarDeclarationNode) {
return modVarDeclarationNode.qualifiers()
.stream().anyMatch(q -> q.text().equals(Qualifier.CONFIGURABLE.getValue()));
}

private ConfigVariable genConfigVariable(ModuleVariableDeclarationNode modVarDeclNode) {
Metadata metadata = new Metadata.Builder<>(null)
.label("Config variables")
.build();

Codedata codedata = new Codedata.Builder<>(null)
.node(NodeKind.ASSIGN)
.lineRange(modVarDeclNode.lineRange())
.build();

Map<String, Property> properties = new LinkedHashMap<>();
TypedBindingPatternNode typedBindingPattern = modVarDeclNode.typedBindingPattern();
properties.put(Property.DATA_TYPE_KEY, property(CONFIG_TYPE, CONFIG_TYPE_DESCRIPTION, Property.ValueType.TYPE
, typedBindingPattern.typeDescriptor().toSourceCode().trim()));
properties.put(Property.VARIABLE_KEY, property(CONFIG_NAME, CONFIG_NAME_DESCRIPTION,
Property.ValueType.IDENTIFIER, typedBindingPattern.bindingPattern().toSourceCode().trim()));
Optional<ExpressionNode> optInitializer = modVarDeclNode.initializer();
String value = "";
if (optInitializer.isPresent()) {
ExpressionNode initializer = optInitializer.get();
if (initializer.kind() != SyntaxKind.REQUIRED_EXPRESSION) {
value = initializer.toSourceCode();
}
}
properties.put("defaultable", property(DEFAULT_VALUE, DEFAULT_VALUE_DESCRIPTION,
Property.ValueType.EXPRESSION, value));

return new ConfigVariable(metadata, codedata, properties);
}

private Property property(String label, String description, Property.ValueType valueType, String value) {
Property.Builder propertyBuilder = Property.Builder.getInstance();
propertyBuilder
.metadata()
.label(label)
.description(description)
.stepOut()
.type(valueType)
.value(value)
.editable();
return propertyBuilder.build();
}

private record ConfigVariable(
Metadata metadata,
Codedata codedata,
Map<String, Property> properties
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2024, 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 io.ballerina.flowmodelgenerator.core.ConfigVariablesManager;
import io.ballerina.flowmodelgenerator.extension.request.ConfigVariablesGetRequest;
import io.ballerina.flowmodelgenerator.extension.response.ConfigVariablesResponse;
import io.ballerina.projects.Document;
import org.ballerinalang.annotation.JavaSPIService;
import org.ballerinalang.langserver.commons.service.spi.ExtendedLanguageServerService;
import org.ballerinalang.langserver.commons.workspace.WorkspaceManager;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.jsonrpc.services.JsonSegment;
import org.eclipse.lsp4j.services.LanguageServer;

import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

@JavaSPIService("org.ballerinalang.langserver.commons.service.spi.ExtendedLanguageServerService")
@JsonSegment("configEditor")
public class ConfigEditorService implements ExtendedLanguageServerService {

private WorkspaceManager workspaceManager;

@Override
public void init(LanguageServer langServer, WorkspaceManager workspaceManager) {
this.workspaceManager = workspaceManager;
}

@Override
public Class<?> getRemoteInterface() {
return null;
}

@JsonRequest
public CompletableFuture<ConfigVariablesResponse> getConfigVariables(ConfigVariablesGetRequest request) {
return CompletableFuture.supplyAsync(() -> {
ConfigVariablesResponse response = new ConfigVariablesResponse();
try {
Path configFile = Path.of(request.configFilePath());
this.workspaceManager.loadProject(configFile);
Optional<Document> document = this.workspaceManager.document(configFile);
if (document.isEmpty()) {
return response;
}

ConfigVariablesManager configVariablesManager = new ConfigVariablesManager();
response.setConfigVariables(configVariablesManager.get(document.get()));
} catch (Throwable e) {
response.setError(e);
}
return response;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024, 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;

/**
* Represents the request to get config variables.
*
* @param configFilePath path of the config file
* @since 1.4.0
*/
public record ConfigVariablesGetRequest(String configFilePath) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024, 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.response;

import com.google.gson.JsonElement;

/**
* Represents the response for the config variables.
*
* @since 1.4.0
*/
public class ConfigVariablesResponse extends AbstractFlowModelResponse {

private JsonElement configVariables;

public void setConfigVariables(JsonElement configVariables) {
this.configVariables = configVariables;
}

public JsonElement configVariables() {
return configVariables;
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
io.ballerina.flowmodelgenerator.extension.FlowModelGeneratorService
io.ballerina.flowmodelgenerator.extension.ExpressionEditorService
io.ballerina.flowmodelgenerator.extension.ConfigEditorService
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2023, 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 io.ballerina.flowmodelgenerator.extension.request.ConfigVariablesGetRequest;
import org.ballerinalang.langserver.BallerinaLanguageServer;
import org.ballerinalang.langserver.util.TestUtil;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/**
* Test cases for the flow model generator service.
*
* @since 1.4.0
*/
public class ConfigVariablesTest extends AbstractLSTest {

@Override
@Test(dataProvider = "data-provider")
public void test(Path config) throws IOException {
Endpoint endpoint = TestUtil.newLanguageServer().withLanguageServer(new BallerinaLanguageServer()).build();
Path configJsonPath = configDir.resolve(config);
ConfigVariablesTestConfig testConfig = gson.fromJson(Files.newBufferedReader(configJsonPath),
ConfigVariablesTestConfig.class);

ConfigVariablesGetRequest request =
new ConfigVariablesGetRequest(sourceDir.resolve(testConfig.configFile()).toAbsolutePath().toString());
JsonObject configVariables = getResponse(endpoint, request);

if (!configVariables.equals(testConfig.configVariables())) {
ConfigVariablesTestConfig updatedConfig = new ConfigVariablesTestConfig(testConfig.configFile(),
configVariables);
// updateConfig(configJsonPath, updatedConfig);
Assert.fail(String.format("Failed test: '%s'", configJsonPath));
}
TestUtil.shutdownLanguageServer(endpoint);
}

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

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

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

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

/**
* Represents the test configuration for the model generator test.
*
* @param configFile Path to config file
* @param configVariables Config variables
* @since 1.4.0
*/
private record ConfigVariablesTestConfig(String configFile, JsonElement configVariables) {

}
}
Loading

0 comments on commit a2ecb9c

Please sign in to comment.