Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several issues in the http service designer form #558

Merged
Show file tree
Hide file tree
Changes from all 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 @@ -35,6 +35,9 @@ public class ServiceModelGeneratorConstants {
public static final String SINGLE_SELECT_VALUE = "SINGLE_SELECT";
public static final String MULTIPLE_SELECT_VALUE = "MULTIPLE_SELECT";

public static final String HTTP_DEFAULT_LISTENER_REF = "default:httpListener";
LakshanWeerasinghe marked this conversation as resolved.
Show resolved Hide resolved
public static final String HTTP_DEFAULT_MODULE = "http.default";

public static final String KAFKA = "kafka";
public static final String HTTP = "http";
public static final String GRAPHQL = "graphql";
Expand All @@ -57,6 +60,7 @@ public class ServiceModelGeneratorConstants {
public static final String VALUE_TYPE_EXPRESSION = "EXPRESSION";
public static final String VALUE_TYPE_IDENTIFIER = "IDENTIFIER";
public static final String VALUE_TYPE_TYPE = "TYPE";
public static final String HTTP_PARAM_TYPE_QUERY = "QUERY";

public static final String TYPE_HTTP_SERVICE_CONFIG = "http:ServiceConfig";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,12 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

Expand All @@ -112,6 +114,7 @@
import static io.ballerina.servicemodelgenerator.extension.Utils.getResourceFunctionModel;
import static io.ballerina.servicemodelgenerator.extension.Utils.getServiceDeclarationNode;
import static io.ballerina.servicemodelgenerator.extension.Utils.importExists;
import static io.ballerina.servicemodelgenerator.extension.Utils.isHttpDefaultListenerAttached;
import static io.ballerina.servicemodelgenerator.extension.Utils.isHttpServiceContractType;
import static io.ballerina.servicemodelgenerator.extension.Utils.populateProperties;
import static io.ballerina.servicemodelgenerator.extension.Utils.updateListenerModel;
Expand Down Expand Up @@ -172,23 +175,32 @@ public CompletableFuture<ListenerDiscoveryResponse> getListeners(ListenerDiscove
Module module = currentPackage.module(ModuleName.from(currentPackage.packageName()));
ModuleId moduleId = module.moduleId();
SemanticModel semanticModel = currentPackage.getCompilation().getSemanticModel(moduleId);
List<String> listeners = getCompatibleListeners(request.moduleName(), semanticModel);
Set<String> listeners = getCompatibleListeners(request.moduleName(), semanticModel);
return new ListenerDiscoveryResponse(listeners);
} catch (Throwable e) {
return new ListenerDiscoveryResponse(e);
}
});
}

private static List<String> getCompatibleListeners(String moduleName, SemanticModel semanticModel) {
return semanticModel.moduleSymbols().stream()
.filter(symbol -> symbol instanceof VariableSymbol)
.map(symbol -> (VariableSymbol) symbol)
.filter(variableSymbol -> variableSymbol.qualifiers().contains(Qualifier.LISTENER))
.filter(variableSymbol -> variableSymbol.typeDescriptor().getModule().isPresent() && variableSymbol
.typeDescriptor().getModule().get().id().moduleName().equals(moduleName))
.map(variableSymbol -> variableSymbol.getName().orElse(""))
.toList();
private static Set<String> getCompatibleListeners(String moduleName, SemanticModel semanticModel) {
Set<String> listeners = new LinkedHashSet<>();
if (ServiceModelGeneratorConstants.HTTP.equals(moduleName)) {
listeners.add(ServiceModelGeneratorConstants.HTTP_DEFAULT_LISTENER_REF);
}

for (Symbol moduleSymbol : semanticModel.moduleSymbols()) {
if (!(moduleSymbol instanceof VariableSymbol variableSymbol)
|| !variableSymbol.qualifiers().contains(Qualifier.LISTENER)) {
continue;
}
Optional<ModuleSymbol> module = variableSymbol.typeDescriptor().getModule();
if (module.isEmpty() || !module.get().id().moduleName().equals(moduleName)) {
continue;
}
variableSymbol.getName().ifPresent(listeners::add);
}
return listeners;
}

/**
Expand Down Expand Up @@ -273,7 +285,7 @@ public CompletableFuture<ServiceModelResponse> getServiceModel(ServiceModelReque
}
SyntaxTree syntaxTree = document.get().syntaxTree();
ModulePartNode modulePartNode = syntaxTree.rootNode();
List<String> listenersList = getCompatibleListeners(request.moduleName(), semanticModel);
Set<String> listenersList = getCompatibleListeners(request.moduleName(), semanticModel);
if (Objects.nonNull(request.listenerName())) {
listener.addValue(request.listenerName());
removeAlreadyDefinedServiceTypes(serviceModel, request.listenerName(), modulePartNode);
Expand All @@ -284,7 +296,7 @@ public CompletableFuture<ServiceModelResponse> getServiceModel(ServiceModelReque
} else {
listener.setValueType(ServiceModelGeneratorConstants.MULTIPLE_SELECT_VALUE);
}
listener.setItems(listenersList);
listener.setItems(listenersList.stream().toList());
}
return new ServiceModelResponse(serviceModel);
} catch (Throwable e) {
Expand Down Expand Up @@ -357,6 +369,14 @@ public CompletableFuture<CommonSourceResponse> addService(ServiceSourceRequest r
String importText = Utils.getImportStmt(service.getOrgName(), service.getModuleName());
edits.add(new TextEdit(Utils.toRange(lineRange.startLine()), importText));
}
if (ServiceModelGeneratorConstants.HTTP.equals(service.getModuleName())
&& isHttpDefaultListenerAttached(service.getListener()) &&
!importExists(node, service.getOrgName(), ServiceModelGeneratorConstants.HTTP_DEFAULT_MODULE)) {
String importText = Utils.getImportStmt(service.getOrgName(),
ServiceModelGeneratorConstants.HTTP_DEFAULT_MODULE);
edits.add(new TextEdit(Utils.toRange(lineRange.startLine()), importText));
}

edits.add(new TextEdit(Utils.toRange(lineRange.endLine()),
ServiceModelGeneratorConstants.LINE_SEPARATOR + serviceDeclaration));
return new CommonSourceResponse(Map.of(request.filePath(), edits));
Expand Down Expand Up @@ -522,15 +542,21 @@ public CompletableFuture<ServiceFromSourceResponse> getServiceFromSource(CommonM
} else {
updateServiceModel(serviceModel, serviceNode, semanticModel);
}
List<String> listenersList = getCompatibleListeners(serviceName.get(), semanticModel);
Set<String> listeners = getCompatibleListeners(serviceName.get(), semanticModel);
List<String> allValues = serviceModel.getListener().getValues();
if (allValues.isEmpty()) {
listeners.add(serviceModel.getListener().getValue());
} else {
listeners.addAll(allValues);
}
Value listener = serviceModel.getListener();
if (!listenersList.isEmpty()) {
if (!listeners.isEmpty()) {
if (serviceName.get().equals(ServiceModelGeneratorConstants.KAFKA)) {
listener.setValueType(ServiceModelGeneratorConstants.SINGLE_SELECT_VALUE);
} else {
listener.setValueType(ServiceModelGeneratorConstants.MULTIPLE_SELECT_VALUE);
}
listener.setItems(listenersList);
listener.setItems(listeners.stream().toList());
}
return new ServiceFromSourceResponse(serviceModel);
});
Expand Down Expand Up @@ -770,6 +796,14 @@ public CompletableFuture<CommonSourceResponse> updateService(ServiceModifierRequ
edits.add(listenerEdit);
}
}
if (ServiceModelGeneratorConstants.HTTP.equals(service.getModuleName()) &&
isHttpDefaultListenerAttached(service.getListener()) &&
!importExists(modulePartNode, service.getOrgName(),
ServiceModelGeneratorConstants.HTTP_DEFAULT_MODULE)) {
String importText = Utils.getImportStmt(service.getOrgName(),
ServiceModelGeneratorConstants.HTTP_DEFAULT_MODULE);
edits.add(new TextEdit(Utils.toRange(lineRange.startLine()), importText));
}
return new CommonSourceResponse(Map.of(request.filePath(), edits));
} catch (Throwable e) {
return new CommonSourceResponse(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ public static Function getFunctionModel(MethodDeclarationNode functionDefinition
SeparatedNodeList<ParameterNode> parameters = functionSignatureNode.parameters();
List<Parameter> parameterModels = new ArrayList<>();
parameters.forEach(parameterNode -> {
Optional<Parameter> parameterModel = getParameterModel(parameterNode);
Optional<Parameter> parameterModel = getParameterModel(parameterNode, isHttp);
parameterModel.ifPresent(parameterModels::add);
});
functionModel.setParameters(parameterModels);
Expand Down Expand Up @@ -508,7 +508,7 @@ public static Function getFunctionModel(FunctionDefinitionNode functionDefinitio
SeparatedNodeList<ParameterNode> parameters = functionSignatureNode.parameters();
List<Parameter> parameterModels = new ArrayList<>();
parameters.forEach(parameterNode -> {
Optional<Parameter> parameterModel = getParameterModel(parameterNode);
Optional<Parameter> parameterModel = getParameterModel(parameterNode, isHttp);
parameterModel.ifPresent(parameterModels::add);
});
functionModel.setParameters(parameterModels);
Expand Down Expand Up @@ -700,18 +700,18 @@ public static Optional<String> getHttpParameterType(NodeList<AnnotationNode> ann
return Optional.empty();
}

public static Optional<Parameter> getParameterModel(ParameterNode parameterNode) {
public static Optional<Parameter> getParameterModel(ParameterNode parameterNode, boolean isHttp) {
if (parameterNode instanceof RequiredParameterNode parameter) {
String paramName = parameter.paramName().get().toString().trim();
Parameter parameterModel = createParameter(paramName, ServiceModelGeneratorConstants.KIND_REQUIRED,
ServiceModelGeneratorConstants.VALUE_TYPE_IDENTIFIER, parameter.typeName().toString().trim(),
parameter.annotations());
parameter.annotations(), isHttp);
return Optional.of(parameterModel);
} else if (parameterNode instanceof DefaultableParameterNode parameter) {
String paramName = parameter.paramName().get().toString().trim();
Parameter parameterModel = createParameter(paramName, ServiceModelGeneratorConstants.KIND_DEFAULTABLE,
ServiceModelGeneratorConstants.VALUE_TYPE_EXPRESSION, parameter.typeName().toString().trim(),
parameter.annotations());
parameter.annotations(), isHttp);
Value defaultValue = parameterModel.getDefaultValue();
defaultValue.setValue(parameter.expression().toString().trim());
defaultValue.setValueType(ServiceModelGeneratorConstants.VALUE_TYPE_EXPRESSION);
Expand All @@ -722,11 +722,18 @@ public static Optional<Parameter> getParameterModel(ParameterNode parameterNode)
}

private static Parameter createParameter(String paramName, String paramKind, String valueType, String typeName,
NodeList<AnnotationNode> annotationNodes) {
NodeList<AnnotationNode> annotationNodes, boolean isHttp) {
Parameter parameterModel = Parameter.getNewParameter();
parameterModel.setMetadata(new MetaData(paramName, paramName));
parameterModel.setKind(paramKind);
getHttpParameterType(annotationNodes).ifPresent(parameterModel::setHttpParamType);
if (isHttp) {
Optional<String> httpParameterType = getHttpParameterType(annotationNodes);
if (httpParameterType.isPresent()) {
parameterModel.setHttpParamType(httpParameterType.get());
} else {
parameterModel.setHttpParamType(ServiceModelGeneratorConstants.HTTP_PARAM_TYPE_QUERY);
}
}
Value type = parameterModel.getType();
type.setValue(typeName);
type.setValueType(ServiceModelGeneratorConstants.VALUE_TYPE_TYPE);
Expand Down Expand Up @@ -1233,4 +1240,17 @@ public static String getExprUri(String sourcePath) {
String exprUriString = "expr" + Paths.get(sourcePath).toUri().toString().substring(4);
return URI.create(exprUriString).toString();
}

/**
* Check if the `default:httpListener` is attached to the service.
*
* @param value the Listener value
* @return true if the `default:httpListener` is attached, false otherwise
*/
public static boolean isHttpDefaultListenerAttached(Value value) {
if (value.getValues().isEmpty()) {
return ServiceModelGeneratorConstants.HTTP_DEFAULT_LISTENER_REF.equals(value.getValue().trim());
}
return value.getValues().contains(ServiceModelGeneratorConstants.HTTP_DEFAULT_LISTENER_REF);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@

package io.ballerina.servicemodelgenerator.extension.response;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Set;

public record ListenerDiscoveryResponse(boolean hasListeners, List<String> listeners, String errorMsg,
public record ListenerDiscoveryResponse(boolean hasListeners, Set<String> listeners, String errorMsg,
String stacktrace) {

public ListenerDiscoveryResponse() {
this(false, new ArrayList<>(), null, null);
this(false, new HashSet<>(), null, null);
}

public ListenerDiscoveryResponse(List<String> listeners) {
public ListenerDiscoveryResponse(Set<String> listeners) {
this(!listeners.isEmpty(), listeners, null, null);
}

public ListenerDiscoveryResponse(Throwable e) {
this(false, new ArrayList<>(), e.toString(), Arrays.toString(e.getStackTrace()));
this(false, new HashSet<>(), e.toString(), Arrays.toString(e.getStackTrace()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ public void testListenerDiscoveryWithEmptyFile() throws ExecutionException, Inte
"ballerina", "http");
CompletableFuture<?> result = serviceEndpoint.request("serviceDesign/getListeners", request);
ListenerDiscoveryResponse response = (ListenerDiscoveryResponse) result.get();
Assert.assertFalse(response.hasListeners());
Assert.assertEquals(response.listeners().size(), 0);
Assert.assertTrue(response.hasListeners());
Assert.assertEquals(response.listeners().size(), 1);
}

@Test(enabled = false)
Expand Down Expand Up @@ -204,7 +204,7 @@ public void testAddHttpService() throws ExecutionException, InterruptedException
ServiceModelResponse modelResponse = (ServiceModelResponse) modelResult.get();
Service service = modelResponse.service();
Assert.assertTrue(Objects.nonNull(service));
service.getListener().setValues(List.of("httpTestListener", "httpsTestListener"));
service.getListener().setValues(List.of("default:httpListener", "httpsTestListener"));
Value designApproach = service.getDesignApproach();
Value selectedApproach = designApproach.getChoices().getFirst();
Value basePath = selectedApproach.getProperty("basePath");
Expand Down Expand Up @@ -523,7 +523,7 @@ public void testUpdateHttpService() throws ExecutionException, InterruptedExcept
ServiceFromSourceResponse sourceResponse = (ServiceFromSourceResponse) sourceResult.get();
Service service = sourceResponse.service();
Assert.assertTrue(Objects.nonNull(service));
service.getListener().setValue("newHttpListener");
service.getListener().setValues(List.of("newHttpListener", "default:httpListener"));
service.getBasePath().setValue("/api/v1/test/new");

ServiceModifierRequest updateRequest = new ServiceModifierRequest(filePath.toAbsolutePath().toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"placeholder": "",
"optional": false,
"advanced": false,
"items": [
"default:httpListener",
"new http:Listener(port = 8080)"
],
"codedata": {
"inListenerInit": false,
"isBasePath": false,
Expand Down Expand Up @@ -371,7 +375,8 @@
"enabled": true,
"editable": false,
"optional": false,
"advanced": false
"advanced": false,
"httpParamType": "QUERY"
}
],
"schema": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
"optional": false,
"advanced": false,
"items": [
"default:globalListener",
"default:httpListener",
"httpListener",
"new http:Listener(port = 8080)",
"githubListener"
],
"codedata": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
"optional": false,
"advanced": false,
"items": [
"default:httpListener",
"httpListener",
"new http:Listener(port = 8080)",
"githubListener"
],
"codedata": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"optional": false,
"advanced": false,
"items": [
"default:httpListener",
"httpListener",
"githubListener"
],
Expand Down Expand Up @@ -375,7 +376,8 @@
"enabled": true,
"editable": false,
"optional": false,
"advanced": false
"advanced": false,
"httpParamType": "QUERY"
}
],
"schema": {
Expand Down
Loading