Skip to content

Commit

Permalink
Add compiler support
Browse files Browse the repository at this point in the history
  • Loading branch information
TharmiganK committed Jun 7, 2024
1 parent 005d667 commit e67eb21
Show file tree
Hide file tree
Showing 20 changed files with 1,440 additions and 74 deletions.
19 changes: 15 additions & 4 deletions ballerina/http_annotation.bal
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
# + treatNilableAsOptional - Treat Nilable parameters as optional
# + openApiDefinition - The generated OpenAPI definition for the HTTP service. This is auto-generated at compile-time if OpenAPI doc auto generation is enabled
# + validation - Enables the inbound payload validation functionalty which provided by the constraint package. Enabled by default
# + serviceType - The service object type which defines the service contract
public type HttpServiceConfig record {|
string host = "b7a.default";
CompressionConfig compression = {};
Expand All @@ -35,6 +36,7 @@ public type HttpServiceConfig record {|
boolean treatNilableAsOptional = true;
byte[] openApiDefinition = [];
boolean validation = true;
typedesc<ServiceContract> serviceType?;
|};

# Configurations for CORS support.
Expand All @@ -55,7 +57,7 @@ public type CorsConfig record {|
|};

# The annotation which is used to configure an HTTP service.
public annotation HttpServiceConfig ServiceConfig on service;
public annotation HttpServiceConfig ServiceConfig on service, type;

# Configuration for an HTTP resource.
#
Expand Down Expand Up @@ -87,7 +89,7 @@ public type HttpPayload record {|
|};

# The annotation which is used to define the Payload resource signature parameter and return parameter.
public annotation HttpPayload Payload on parameter, return;
public const annotation HttpPayload Payload on parameter, return;

# Configures the typing details type of the Caller resource signature parameter.
#
Expand All @@ -108,13 +110,13 @@ public type HttpHeader record {|
|};

# The annotation which is used to define the Header resource signature parameter.
public annotation HttpHeader Header on parameter;
public const annotation HttpHeader Header on parameter;

# Defines the query resource signature parameter.
public type HttpQuery record {||};

# The annotation which is used to define the query resource signature parameter.
public annotation HttpQuery Query on parameter;
public const annotation HttpQuery Query on parameter;

# Defines the HTTP response cache configuration. By default the `no-cache` directive is setted to the `cache-control`
# header. In addition to that `etag` and `last-modified` headers are also added for cache validation.
Expand Down Expand Up @@ -152,3 +154,12 @@ public type HttpCacheConfig record {|
# Success(2XX) `StatusCodeResponses` return types. Default annotation adds `must-revalidate,public,max-age=3600` as
# `cache-control` header in addition to `etag` and `last-modified` headers.
public annotation HttpCacheConfig Cache on return;

# Service contract configuration
# + basePath - Base path for generated service contract
public type ServiceContractConfiguration record {|
string basePath;
|};

# Annotation for mapping service contract information to a Ballerina service type.
public const annotation ServiceContractConfiguration ServiceContractConfig on type;
5 changes: 5 additions & 0 deletions ballerina/http_types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public type Service distinct service object {

};

# The HTTP service contract type.
public type ServiceContract distinct service object {
*Service;
};

# The types of data values that are expected by the HTTP `client` to return after the data binding operation.
public type TargetType typedesc<Response|anydata>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ private Constants() {}

public static final String BALLERINA = "ballerina";
public static final String HTTP = "http";
public static final String SERVICE_CONTRACT_CONFIG = "ServiceContractConfiguration";
public static final String SERVICE_CONTRACT_TYPE = "ServiceContract";
public static final String HTTP_SERVICE_TYPE = "Service";
public static final String SERVICE_TYPE= "serviceType";
public static final String SERVICE_KEYWORD = "service";
public static final String REMOTE_KEYWORD = "remote";
public static final String RESOURCE_KEYWORD = "resource";
Expand Down Expand Up @@ -72,8 +76,12 @@ private Constants() {}
public static final String OBJECT = "object";
public static final String HEADER_OBJ_NAME = "Headers";
public static final String PAYLOAD_ANNOTATION = "Payload";
public static final String HEADER_ANNOTATION = "Header";
public static final String QUERY_ANNOTATION = "Query";
public static final String CALLER_ANNOTATION = "Caller";
public static final String CACHE_ANNOTATION = "Cache";
public static final String SERVICE_CONFIG_ANNOTATION = "ServiceConfig";
public static final String SERVICE_CONTRACT_CONFIG_ANNOTATION = "ServiceContractConfig";
public static final String MEDIA_TYPE_SUBTYPE_PREFIX = "mediaTypeSubtypePrefix";
public static final String INTERCEPTABLE_SERVICE = "InterceptableService";
public static final String RESOURCE_CONFIG_ANNOTATION = "ResourceConfig";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@
import io.ballerina.projects.plugins.CompilerPluginContext;
import io.ballerina.projects.plugins.codeaction.CodeAction;
import io.ballerina.projects.plugins.completion.CompletionProvider;
import io.ballerina.stdlib.http.compiler.codeaction.AddBasePathCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddHeaderParameterCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddInterceptorRemoteMethodCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddInterceptorResourceMethodCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddPayloadParameterCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddResponseCacheConfigCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.AddResponseContentTypeCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.ChangeBasePathCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.ChangeHeaderParamTypeToStringArrayCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.ChangeHeaderParamTypeToStringCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.ChangeReturnTypeWithCallerCodeAction;
import io.ballerina.stdlib.http.compiler.codeaction.ImplementServiceContract;
import io.ballerina.stdlib.http.compiler.codemodifier.HttpServiceModifier;
import io.ballerina.stdlib.http.compiler.completion.HttpServiceBodyContextProvider;

Expand Down Expand Up @@ -60,7 +63,10 @@ private List<CodeAction> getCodeActions() {
new AddResponseContentTypeCodeAction(),
new AddResponseCacheConfigCodeAction(),
new AddInterceptorResourceMethodCodeAction(),
new AddInterceptorRemoteMethodCodeAction()
new AddInterceptorRemoteMethodCodeAction(),
new ImplementServiceContract(),
new AddBasePathCodeAction(),
new ChangeBasePathCodeAction()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ public enum HttpDiagnosticCodes {
HTTP_101("HTTP_101", "remote methods are not allowed in http:Service", ERROR),
HTTP_102("HTTP_102", "invalid resource method return type: expected '" + ALLOWED_RETURN_UNION +
"', but found '%s'", ERROR),
HTTP_103("HTTP_103", "invalid resource method annotation type: expected 'http:" + RESOURCE_CONFIG_ANNOTATION +
"', but found '%s'", ERROR),
HTTP_104("HTTP_104", "invalid annotation type on param '%s': expected one of the following types: " +
"'http:Payload', 'http:CallerInfo', 'http:Header', 'http:Query'", ERROR),
HTTP_105("HTTP_105", "invalid resource parameter '%s'", ERROR),
Expand Down Expand Up @@ -110,11 +108,30 @@ public enum HttpDiagnosticCodes {
HTTP_151("HTTP_151", "ambiguous types for parameter '%s' and '%s'. Use annotations to avoid ambiguity", ERROR),
HTTP_152("HTTP_152", "invalid union type for default payload param: '%s'. Use basic structured anydata types",
ERROR),
HTTP_153("HTTP_153", "'http:ServiceConfig' annotation is not allowed for service declaration implemented via the " +
"'http:ServiceContract' type. The HTTP annotations are inferred from the service contract type", ERROR),
HTTP_154("HTTP_154", "base path not found for the service defined with 'http:ServiceContract' type. " +
"Expected base path is '%s'", ERROR),
HTTP_155("HTTP_155", "invalid base path found for the service defined with 'http:ServiceContract' type." +
" Expected base path is '%s', but found '%s'", ERROR),
HTTP_156("HTTP_156", "invalid service type descriptor found in `http:ServiceConfig' annotation. " +
"Expected service type: '%s' but found: '%s'", ERROR),
HTTP_157("HTTP_157", "`serviceType` is not allowed in the service which is not implemented " +
"via the 'http:ServiceContract' type", ERROR),
HTTP_158("HTTP_158", "resource function which is not defined in the service contract type: '%s'," +
" is not allowed", ERROR),
HTTP_159("HTTP_159", "'http:ResourceConfig' annotation is not allowed for resource function implemented via the " +
"'http:ServiceContract' type. The HTTP annotations are inferred from the service contract type", ERROR),
HTTP_160("HTTP_160", "'%s' annotation is not allowed for resource function implemented via the " +
"'http:ServiceContract' type. The HTTP annotations are inferred from the service contract type", ERROR),
HTTP_161("HTTP_161", "'http:ServiceContractConfig' annotation is only allowed for service object type " +
"including 'http:ServiceContract' type", ERROR),

HTTP_HINT_101("HTTP_HINT_101", "Payload annotation can be added", INTERNAL),
HTTP_HINT_102("HTTP_HINT_102", "Header annotation can be added", INTERNAL),
HTTP_HINT_103("HTTP_HINT_103", "Response content-type can be added", INTERNAL),
HTTP_HINT_104("HTTP_HINT_104", "Response cache configuration can be added", INTERNAL);
HTTP_HINT_104("HTTP_HINT_104", "Response cache configuration can be added", INTERNAL),
HTTP_HINT_105("HTTP_HINT_105", "Service contract: '%s', can be implemented", INTERNAL);

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public static void validateResource(SyntaxNodeAnalysisContext ctx, FunctionDefin
if (isRequestErrorInterceptor(type)) {
extractAndValidateMethodAndPath(ctx, member);
}
HttpResourceValidator.extractInputParamTypeAndValidate(ctx, member, isRequestErrorInterceptor(type),
HttpResourceFunctionNode functionNode = new HttpResourceFunctionNode(member);
HttpResourceValidator.extractInputParamTypeAndValidate(ctx, functionNode, isRequestErrorInterceptor(type),
typeSymbols);
HttpCompilerPluginUtil.extractInterceptorReturnTypeAndValidate(ctx, typeSymbols, member,
HttpDiagnosticCodes.HTTP_126);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.stdlib.http.compiler;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.api.symbols.Symbol;
import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
import io.ballerina.compiler.syntax.tree.FunctionSignatureNode;
import io.ballerina.compiler.syntax.tree.IdentifierToken;
import io.ballerina.compiler.syntax.tree.MetadataNode;
import io.ballerina.compiler.syntax.tree.MethodDeclarationNode;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeList;
import io.ballerina.compiler.syntax.tree.NodeVisitor;
import io.ballerina.compiler.syntax.tree.SyntaxKind;
import io.ballerina.tools.diagnostics.Location;

import java.util.Optional;

public class HttpResourceFunctionNode {

Node functionNode;
Optional<MetadataNode> metadata;
NodeList<Node> resourcePath;
FunctionSignatureNode functionSignatureNode;
IdentifierToken functionName;

public HttpResourceFunctionNode(FunctionDefinitionNode functionDefinitionNode) {
functionNode = functionDefinitionNode;
metadata = functionDefinitionNode.metadata();
resourcePath = functionDefinitionNode.relativeResourcePath();
functionSignatureNode = functionDefinitionNode.functionSignature();
functionName = functionDefinitionNode.functionName();
}

public HttpResourceFunctionNode(MethodDeclarationNode methodDeclarationNode) {
functionNode = methodDeclarationNode;
metadata = methodDeclarationNode.metadata();
resourcePath = methodDeclarationNode.relativeResourcePath();
functionSignatureNode = methodDeclarationNode.methodSignature();
functionName = methodDeclarationNode.methodName();
}

public void accept(NodeVisitor nodeVisitor) {
functionNode.accept(nodeVisitor);
}

public Optional<MetadataNode> metadata() {
return metadata;
}

public NodeList<Node> relativeResourcePath() {
return resourcePath;
}

public FunctionSignatureNode functionSignature() {
return functionSignatureNode;
}

public IdentifierToken functionName() {
return functionName;
}

public Location location() {
return functionNode.location();
}

public Optional<FunctionDefinitionNode> getFunctionDefinitionNode() {
return functionNode.kind().equals(SyntaxKind.FUNCTION_DEFINITION) ?
Optional.of((FunctionDefinitionNode) functionNode) : Optional.empty();
}

public Optional<Symbol> getSymbol(SemanticModel semanticModel) {
return semanticModel.symbol(functionNode);
}
}
Loading

0 comments on commit e67eb21

Please sign in to comment.