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

Modify based on header-review #2

Merged
merged 1 commit into from
Oct 13, 2022
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
32 changes: 16 additions & 16 deletions ballerina-tests/tests/main.bal
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@ listener af:HttpListener ep2 = new ();
treatNilableAsOptional: false
}
service /httpHeader on ep2 {
resource function post nonTreatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? {
return Hoste;
resource function post nonTreatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? hoste) returns string? {
return hoste;
}

resource function post nonTreatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string {
return Hoste;
resource function post nonTreatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string hoste) returns string {
return hoste;

}

resource function post nonTreatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string {
return Hos;
resource function post nonTreatNilAsOpt\-nonNil\-HeaderTest(@http:Header string hos) returns string {
return hos;

}

resource function post nonTreatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? {
return Hos;
resource function post nonTreatNilAsOpt\-Nil\-HeaderTest(@http:Header string? hos) returns string? {
return hos;

}
}
Expand Down Expand Up @@ -97,13 +97,13 @@ service /httpHeader on ep3 {

}

resource function post treatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string {
return Hoste;
resource function post treatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string hoste) returns string {
return hoste;

}

resource function post treatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string {
return Hos;
resource function post treatNilAsOpt\-nonNil\-HeaderTest(@http:Header string hos) returns string {
return hos;

}

Expand All @@ -112,13 +112,13 @@ service /httpHeader on ep3 {

}

resource function post treatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? {
return Hoste;
resource function post treatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? hoste) returns string? {
return hoste;

}

resource function post treatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? {
return Hos;
resource function post treatNilAsOpt\-Nil\-HeaderTest(@http:Header string? hos) returns string? {
return hos;

}
}
Expand Down
4 changes: 2 additions & 2 deletions ballerina-tests/tests/resources/httpHeaderTest.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"test":[
"12, 13, 14"
],
"Hos":[
"hos":[
""
],
"X-ARR-LOG-ID":[
Expand Down Expand Up @@ -97,7 +97,7 @@
"Max-Forwards":"10",
"User-Agent":"ballerina",
"test":"12, 13, 14",
"Hos": "",
"hos": "",
"X-ARR-LOG-ID":"77ef8e37-520e-417b-a972-30746b719b60",
"CLIENT-IP":"45.121.88.92:53880",
"DISGUISED-HOST":"az-func-http-test.azurewebsites.net",
Expand Down
10 changes: 5 additions & 5 deletions ballerina-tests/tests/test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function nnonTreatNilAsOpt\-Nil\-noHeaderTest() returns error? {
string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-Nil-noHeaderTest");
json readJson = check value:fromJsonString(replacedString);
json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-Nil-noHeaderTest", readJson);
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
test:assertEquals(resp, expectedResp);
}

Expand All @@ -108,7 +108,7 @@ function treatNilAsOpt\-nonNil\-noHeaderTest() returns error? {
string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-noHeaderTest");
json readJson = check value:fromJsonString(replacedString);
json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-noHeaderTest", readJson);
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
test:assertEquals(resp, expectedResp);
}

Expand All @@ -120,7 +120,7 @@ function treatNilAsOpt\-nonNil\-HeaderTest() returns error? {
string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-HeaderTest");
json readJson = check value:fromJsonString(replacedString);
json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-HeaderTest", readJson);
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
test:assertEquals(resp, expectedResp);
}

Expand Down Expand Up @@ -168,7 +168,7 @@ function nonTreatNilAsOpt\-nonNil\-noHeaderTest() returns error? {
string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-noHeaderTest");
json readJson = check value:fromJsonString(replacedString);
json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-noHeaderTest", readJson);
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
test:assertEquals(resp, expectedResp);
}

Expand All @@ -180,7 +180,7 @@ function nonTreatNilAsOpt\-nonNil\-HeaderTest() returns error? {
string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-HeaderTest");
json readJson = check value:fromJsonString(replacedString);
json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-HeaderTest", readJson);
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null};
test:assertEquals(resp, expectedResp);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,17 @@ public void headerAnnotationTest() {
Assert.assertEquals(((Diagnostic) diagnostics[11]).diagnosticInfo().messageFormat(), diagnosticMessage11);
Assert.assertEquals(((Diagnostic) diagnostics[12]).diagnosticInfo().messageFormat(), diagnosticMessage12);
}

@Test
public void httpServiceConfigTest() {
BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("http-service-config"));
PackageCompilation compilation = project.currentPackage().getCompilation();
DiagnosticResult diagnosticResult = compilation.diagnosticResult();
Object[] diagnostics = diagnosticResult.warnings().toArray();
Assert.assertEquals(diagnosticResult.warningCount(), 2);
String diagnosticMessage = "'treatNilableAsOptional' is the only @http:serviceConfig " +
"field supported by Azure Function at the moment";
Assert.assertEquals(((Diagnostic) diagnostics[0]).diagnosticInfo().messageFormat(), diagnosticMessage);
Assert.assertEquals(((Diagnostic) diagnostics[1]).diagnosticInfo().messageFormat(), diagnosticMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
org = "luheerathan"
name = "http_header_annotation"
version = "0.1.0"
distribution = "2201.1.0"

[build-options]
observabilityIncluded = true

[[dependency]]
org = "ballerinax"
name = "azure_functions"
version = "3.0.0-alpha.1"
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ballerinax/azure_functions as af;
import ballerina/http;

listener af:HttpListener ep1 = new ();
@http:ServiceConfig{treatNilableAsOptional: true, host : "b7a.default"}
service "hello" on ep1 {
resource function get test1(@http:Header string? hoste) returns string? {
return hoste;
}
}

listener af:HttpListener ep2 = new ();
@http:ServiceConfig{host : "b7a.default"}
service "hello" on ep2 {
resource function get test1(@http:Header string? hoste) returns string? {
return hoste;
}
}

listener af:HttpListener ep3 = new ();
@http:ServiceConfig{treatNilableAsOptional: true}
service "hello" on ep3 {
resource function get test1(@http:Header string? hoste) returns string? {
return hoste;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.ballerina.tools.diagnostics.DiagnosticSeverity;

import static io.ballerina.tools.diagnostics.DiagnosticSeverity.ERROR;
import static io.ballerina.tools.diagnostics.DiagnosticSeverity.WARNING;

/**
* {@code DiagnosticCodes} is used to hold diagnostic codes.
Expand All @@ -37,7 +38,9 @@ public enum AzureDiagnosticCodes {
AF_005("AF_005", "invalid intersection type : '%s'. Only readonly type is allowed", ERROR),
AF_006("AF_006", "rest fields are not allowed for header binding records. Use 'http:Headers' type to access " +
"all headers", ERROR),
AF_007("AF_007", "invalid multiple resource parameter annotations for '%s'", ERROR);
AF_007("AF_007", "invalid multiple resource parameter annotations for '%s'", ERROR),
AF_008("AF_008", "'treatNilableAsOptional' is the only @http:serviceConfig field supported by Azure " +
"Function at the moment", WARNING);

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.ballerinax.azurefunctions;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.NodeVisitor;
import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode;
import org.ballerinax.azurefunctions.service.ServiceHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
public class Constants {

public static final String AZURE_FUNCTIONS_MODULE_NAME = "azure_functions";
public static final String COLON = ":";

public static final String HTTP = "http";
public static final String HEADER_ANNOTATION_TYPE = "HttpHeader";
public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax";
Expand Down Expand Up @@ -64,4 +66,7 @@ public class Constants {
public static final String TASKS_FILE_NAME = "tasks.json";

public static final String FUNCTION_ANNOTATION = "Function";
public static final String SERVICE_CONFIG_ANNOTATION = "ServiceConfig";
public static final String TREAT_NILABLE_AS_OPTIONAL = "treatNilableAsOptional";

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,19 @@
import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol;
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.compiler.api.symbols.UnionTypeSymbol;
import io.ballerina.compiler.syntax.tree.AnnotationNode;
import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
import io.ballerina.compiler.syntax.tree.IdentifierToken;
import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode;
import io.ballerina.compiler.syntax.tree.MappingFieldNode;
import io.ballerina.compiler.syntax.tree.MetadataNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeList;
import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext;
import io.ballerina.tools.diagnostics.DiagnosticFactory;
import io.ballerina.tools.diagnostics.DiagnosticInfo;
import io.ballerina.tools.diagnostics.Location;
import org.ballerinax.azurefunctions.AzureDiagnosticCodes;
import org.wso2.ballerinalang.compiler.diagnostic.properties.BSymbolicProperty;
Expand All @@ -47,9 +54,13 @@
import java.util.Optional;
import java.util.stream.Collectors;

import static org.ballerinax.azurefunctions.AzureDiagnosticCodes.AF_008;
import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS_MODULE_NAME;
import static org.ballerinax.azurefunctions.Constants.COLON;
import static org.ballerinax.azurefunctions.Constants.HEADER_ANNOTATION_TYPE;
import static org.ballerinax.azurefunctions.Constants.HTTP;
import static org.ballerinax.azurefunctions.Constants.SERVICE_CONFIG_ANNOTATION;
import static org.ballerinax.azurefunctions.Constants.TREAT_NILABLE_AS_OPTIONAL;
import static org.ballerinax.azurefunctions.Util.updateDiagnostic;

/**
Expand All @@ -76,7 +87,36 @@ private static void validateResourceFunction(SyntaxNodeAnalysisContext ctx, Func

private static void extractServiceAnnotationAndValidate(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext,
ServiceDeclarationNode serviceDeclarationNode) {
//TODO : Validate service annotation fields
//HTTP serviceconfig validation currently supports only for treatNilableAsTrue field
Optional<MetadataNode> metadataNodeOptional = serviceDeclarationNode.metadata();
if (metadataNodeOptional.isEmpty()) {
return;
}
NodeList<AnnotationNode> annotations = metadataNodeOptional.get().annotations();
for (AnnotationNode annotation : annotations) {
Node annotReference = annotation.annotReference();
String annotName = annotReference.toString();
Optional<MappingConstructorExpressionNode> annotValue = annotation.annotValue();
if (annotReference.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) {
continue;
}
String[] annotStrings = annotName.split(COLON);
if (SERVICE_CONFIG_ANNOTATION.equals(annotStrings[annotStrings.length - 1].trim())
&& (annotValue.isPresent())) {
MappingConstructorExpressionNode mapping = annotValue.get();
NodeList fields = mapping.fields();
if (fields.size() == 1) {
MappingFieldNode field = (MappingFieldNode) fields.get(0);
String fieldName = ((IdentifierToken) (field.children()).get(0)).text();
if (!TREAT_NILABLE_AS_OPTIONAL.equals(fieldName)) {
warnInvalidServiceConfig(syntaxNodeAnalysisContext, field);
}
} else {
warnInvalidServiceConfig(syntaxNodeAnalysisContext, mapping);
}
}
}

}

private static void validateInputParameters(SyntaxNodeAnalysisContext ctx, FunctionDefinitionNode member) {
Expand All @@ -90,9 +130,6 @@ private static void validateInputParameters(SyntaxNodeAnalysisContext ctx, Funct
if (parametersOptional.isEmpty()) {
return;
}
if (parametersOptional.get().size() == 0) {
return;
}

for (ParameterSymbol param : parametersOptional.get()) {
Optional<Location> paramLocationOptional = param.getLocation();
Expand Down Expand Up @@ -315,6 +352,11 @@ private static void reportInvalidMultipleAnnotation(SyntaxNodeAnalysisContext ct
String paramName) {
updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_007, paramName);
}

private static void warnInvalidServiceConfig(SyntaxNodeAnalysisContext ctx, Node node) {
DiagnosticInfo diagInfo = new DiagnosticInfo(AF_008.getCode(), AF_008.getMessage(), AF_008.getSeverity());
ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagInfo, node.location()));
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ private Optional<HeaderParameter> processHeaderParam(ResourceMethodType resource
return Optional.of(new HeaderParameter(i, parameter, headerParam));
} else {
throw new RuntimeException("Header annotation can have only one name field.");
//TODO :- add proper exception
}
}
return Optional.empty();
Expand Down