Skip to content

Commit

Permalink
Merge pull request #929 from ayeshLK/main
Browse files Browse the repository at this point in the history
Fix compiler plugin validation not allowing listener port to be a pre-defined variable
  • Loading branch information
ayeshLK committed May 2, 2024
2 parents 4617ee8 + 0a179b9 commit cd8842f
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 11 deletions.
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.9.0-20240410-095500-2653a74d"
distribution-version = "2201.9.0-20240425-195200-d5ce8c72"

[[package]]
org = "ballerina"
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- [`websubhub` compiler plugin does not allow listener port to be a pre-defined variable](https://github.com/ballerina-platform/ballerina-library/issues/6339)

## [1.9.1] - 2023-09-14

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,18 @@ public void testCompilerPluginForListenerImplicitInitWithCheckExpr() {
Assert.assertEquals(diagnosticInfo.code(), expectedCode.getCode());
Assert.assertEquals(diagnostic.message(), expectedCode.getDescription());
}

@Test
public void testCompilerPluginForListenerInitWithPortConfig() {
Package currentPackage = loadPackage("sample_23");
PackageCompilation compilation = currentPackage.getCompilation();
DiagnosticResult diagnosticResult = compilation.diagnosticResult();
List<Diagnostic> errorDiagnostics = diagnosticResult.diagnostics().stream()
.filter(d -> DiagnosticSeverity.ERROR.equals(d.diagnosticInfo().severity()))
.toList();
Assert.assertEquals(errorDiagnostics.size(), 0);
}

private void validateErrorsForInvalidReadonlyTypes(WebSubHubDiagnosticCodes expectedCode, Diagnostic diagnostic,
String typeDesc, String remoteMethodName) {
DiagnosticInfo info = diagnostic.diagnosticInfo();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
org = "websubhub_test"
name = "sample_23"
version = "0.1.0"

[build-options]
observabilityIncluded = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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.

import ballerina/websubhub;

final readonly & string[] TOPICS = ["test", "test1"];

configurable int port = 9090;

listener websubhub:Listener ln1 = new(port, {
secureSocket: {
key: {
path: "tests/resources/ballerinaKeystore.pkcs12",
password: "ballerina"
}
}
});

listener websubhub:Listener ln2 = check new(listenTo = port, config = {
secureSocket: {
key: {
path: "tests/resources/ballerinaKeystore.pkcs12",
password: "ballerina"
}
}
});

listener websubhub:Listener ln3 = check new(listenTo = port);

listener websubhub:Listener ln4 = check new(listenTo = 9090, config = {
secureSocket: {
key: {
path: "tests/resources/ballerinaKeystore.pkcs12",
password: "ballerina"
}
}
});

service /websubhub on new websubhub:Listener(port, {
secureSocket: {
key: {
path: "tests/resources/ballerinaKeystore.pkcs12",
password: "ballerina"
}
}
}) {
isolated remote function onRegisterTopic(websubhub:TopicRegistration message)
returns websubhub:TopicRegistrationSuccess|websubhub:TopicRegistrationError {
if TOPICS.indexOf(message.topic) is () {
return websubhub:TOPIC_REGISTRATION_SUCCESS;
} else {
return websubhub:TOPIC_REGISTRATION_ERROR;
}
}

isolated remote function onDeregisterTopic(websubhub:TopicDeregistration message)
returns websubhub:TopicDeregistrationSuccess|websubhub:TopicDeregistrationError {
if TOPICS.indexOf(message.topic) !is () {
return websubhub:TOPIC_DEREGISTRATION_SUCCESS;
} else {
return websubhub:TOPIC_DEREGISTRATION_ERROR;
}
}

isolated remote function onUpdateMessage(websubhub:UpdateMessage message)
returns websubhub:Acknowledgement|websubhub:UpdateMessageError {
if TOPICS.indexOf(message.hubTopic) !is () {
return websubhub:ACKNOWLEDGEMENT;
} else {
return websubhub:UPDATE_MESSAGE_ERROR;
}
}

isolated remote function onSubscription(websubhub:Subscription message) returns websubhub:SubscriptionAccepted {
return websubhub:SUBSCRIPTION_ACCEPTED;
}

isolated remote function onSubscriptionValidation(websubhub:Subscription message)
returns websubhub:SubscriptionDeniedError? {
if TOPICS.indexOf(message.hubTopic) is () {
return websubhub:SUBSCRIPTION_DENIED_ERROR;
}
return;
}

isolated remote function onSubscriptionIntentVerified(websubhub:VerifiedSubscription message) {
}

isolated remote function onUnsubscription(websubhub:Unsubscription message)
returns websubhub:UnsubscriptionAccepted {
return websubhub:UNSUBSCRIPTION_ACCEPTED;
}

isolated remote function onUnsubscriptionValidation(websubhub:Unsubscription message)
returns websubhub:UnsubscriptionDeniedError? {
if TOPICS.indexOf(message.hubTopic) !is () {
return websubhub:UNSUBSCRIPTION_DENIED_ERROR;
}
return;
}

isolated remote function onUnsubscriptionIntentVerified(websubhub:VerifiedUnsubscription msg) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@

package io.ballerina.stdlib.websubhub.task;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.api.symbols.Symbol;
import io.ballerina.compiler.api.symbols.SymbolKind;
import io.ballerina.compiler.api.symbols.TypeDescKind;
import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol;
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.api.symbols.VariableSymbol;
import io.ballerina.compiler.syntax.tree.ExplicitNewExpressionNode;
import io.ballerina.compiler.syntax.tree.FunctionArgumentNode;
import io.ballerina.compiler.syntax.tree.ImplicitNewExpressionNode;
import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode;
import io.ballerina.compiler.syntax.tree.NodeLocation;
import io.ballerina.compiler.syntax.tree.NamedArgumentNode;
import io.ballerina.compiler.syntax.tree.PositionalArgumentNode;
import io.ballerina.compiler.syntax.tree.RestArgumentNode;
import io.ballerina.compiler.syntax.tree.SeparatedNodeList;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.compiler.syntax.tree.TypeDescriptorNode;
Expand Down Expand Up @@ -70,7 +74,7 @@ private void validateExplicitNewListener(SyntaxNodeAnalysisContext context, Expl
return;
}
SeparatedNodeList<FunctionArgumentNode> functionArgs = node.parenthesizedArgList().arguments();
verifyListenerArgType(context, node.location(), functionArgs);
verifyListenerArgType(context, functionArgs);
}

private void validateImplicitNewListener(SyntaxNodeAnalysisContext context, ImplicitNewExpressionNode node) {
Expand All @@ -92,7 +96,7 @@ private void validateImplicitNewListener(SyntaxNodeAnalysisContext context, Impl
}
if (node.parenthesizedArgList().isPresent()) {
SeparatedNodeList<FunctionArgumentNode> functionArgs = node.parenthesizedArgList().get().arguments();
verifyListenerArgType(context, node.location(), functionArgs);
verifyListenerArgType(context, functionArgs);
}
}

Expand All @@ -107,17 +111,33 @@ private Optional<ListenerDeclarationNode> getImplicitListenerNode(ImplicitNewExp
return Optional.empty();
}

private void verifyListenerArgType(SyntaxNodeAnalysisContext context, NodeLocation location,
private void verifyListenerArgType(SyntaxNodeAnalysisContext context,
SeparatedNodeList<FunctionArgumentNode> functionArgs) {
// two args are valid only if the first arg is numeric (i.e, port and config)
if (functionArgs.size() > 1) {
PositionalArgumentNode firstArg = (PositionalArgumentNode) functionArgs.get(0);
FunctionArgumentNode secondArg = functionArgs.get(1);
SyntaxKind firstArgSyntaxKind = firstArg.expression().kind();
if (firstArgSyntaxKind != SyntaxKind.NUMERIC_LITERAL) {
WebSubHubDiagnosticCodes errorCode = WebSubHubDiagnosticCodes.WEBSUBHUB_101;
updateContext(context, errorCode, secondArg.location());
Optional<Symbol> firstArgSymbolOpt = getFirstListenerArg(context.semanticModel(), functionArgs.get(0));
if (firstArgSymbolOpt.isEmpty()) {
return;
}
Symbol firstArgSymbol = firstArgSymbolOpt.get();
if (SymbolKind.VARIABLE.equals(firstArgSymbol.kind())) {
VariableSymbol variable = (VariableSymbol) firstArgSymbol;
if (TypeDescKind.INT.equals(variable.typeDescriptor().typeKind())) {
return;
}
}
FunctionArgumentNode secondArg = functionArgs.get(1);
updateContext(context, WebSubHubDiagnosticCodes.WEBSUBHUB_101, secondArg.location());
}
}

private Optional<Symbol> getFirstListenerArg(SemanticModel semanticModel, FunctionArgumentNode firstArg) {
if (SyntaxKind.POSITIONAL_ARG.equals(firstArg.kind())) {
return semanticModel.symbol(((PositionalArgumentNode) firstArg).expression());
} else if (SyntaxKind.NAMED_ARG.equals(firstArg.kind())) {
return semanticModel.symbol(((NamedArgumentNode) firstArg).expression());
} else {
return semanticModel.symbol(((RestArgumentNode) firstArg).expression());
}
}
}

0 comments on commit cd8842f

Please sign in to comment.