Skip to content

Commit

Permalink
Merge pull request #7 from HasithaAthukorala/service-types
Browse files Browse the repository at this point in the history
Add service types generation
  • Loading branch information
HasithaAthukorala authored Sep 22, 2021
2 parents 4f7eb3b + 719d62c commit 23ca9e6
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.ballerina.asyncapi.codegenerator.configuration.Constants;
import io.ballerina.asyncapi.codegenerator.controller.Controller;
import io.ballerina.asyncapi.codegenerator.controller.SchemaController;
import io.ballerina.asyncapi.codegenerator.controller.ServiceTypesController;
import io.ballerina.asyncapi.codegenerator.repository.FileRepository;
import io.ballerina.asyncapi.codegenerator.repository.FileRepositoryImpl;

Expand All @@ -42,7 +43,7 @@ public void generate() throws BallerinaAsyncApiException {
String asyncApiSpecJson;
if (specPath.endsWith(".json")) {
asyncApiSpecJson = asyncApiSpecYaml;
} else if (specPath.endsWith("yaml")) {
} else if (specPath.endsWith("yaml") || specPath.endsWith("yml")) {
try {
asyncApiSpecJson = convertYamlToJson(asyncApiSpecYaml);
} catch (JsonProcessingException e) {
Expand All @@ -51,10 +52,12 @@ public void generate() throws BallerinaAsyncApiException {
} else {
throw new BallerinaAsyncApiException("Unknown file type: ".concat(specPath));
}
String balTemplate = fileRepository.getFileContentFromResources(Constants.DATA_TYPES_BAL_TEMPLATE_FILE_NAME);

Controller schemaController = new SchemaController();
schemaController.generateBalCode(asyncApiSpecJson, balTemplate);
schemaController.generateBalCode(asyncApiSpecJson, "");

Controller serviceTypesController = new ServiceTypesController();
serviceTypesController.generateBalCode(asyncApiSpecJson, "");
}

String convertYamlToJson(String yaml) throws JsonProcessingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public final class Constants {
public static final String FLOAT = "float";
public static final String DOUBLE = "double";

public static final String X_BALLERINA_EVENT_TYPE = "x-ballerina-event-type";
public static final String X_BALLERINA_SERVICE_TYPE = "x-ballerina-service-type";

private Constants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.SyntaxTree;
import io.ballerina.compiler.syntax.tree.TypeDefinitionNode;
import io.ballerina.tools.text.TextDocuments;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.asyncapi.codegenerator.controller;

import io.apicurio.datamodels.Library;
import io.apicurio.datamodels.asyncapi.models.AaiChannelItem;
import io.apicurio.datamodels.asyncapi.models.AaiDocument;
import io.apicurio.datamodels.asyncapi.models.AaiMessage;
import io.apicurio.datamodels.asyncapi.v2.models.Aai20Document;
import io.ballerina.asyncapi.codegenerator.configuration.BallerinaAsyncApiException;
import io.ballerina.asyncapi.codegenerator.configuration.Constants;
import io.ballerina.asyncapi.codegenerator.usecase.ExtractServiceTypesFromSpec;
import io.ballerina.asyncapi.codegenerator.usecase.GenerateServiceTypeNode;
import io.ballerina.asyncapi.codegenerator.usecase.UseCase;
import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode;
import io.ballerina.compiler.syntax.tree.ModulePartNode;
import io.ballerina.compiler.syntax.tree.SyntaxTree;
import io.ballerina.tools.text.TextDocuments;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ballerinalang.formatter.core.Formatter;
import org.ballerinalang.formatter.core.FormatterException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServiceTypesController implements Controller {
private static final Logger logger = LogManager.getLogger(ServiceTypesController.class);

@Override
public void generateBalCode(String spec, String balTemplate) throws BallerinaAsyncApiException {
AaiDocument asyncApiSpec = (Aai20Document) Library.readDocumentFromJSONString(spec);

UseCase extractServiceTypes = new ExtractServiceTypesFromSpec(asyncApiSpec);
Map<String, List<String>> serviceTypes = extractServiceTypes.execute();

List<ModuleMemberDeclarationNode> serviceNodes = new ArrayList<>();
for (Map.Entry<String, List<String>> service : serviceTypes.entrySet()) {
UseCase generateServiceTypeNode = new GenerateServiceTypeNode(service.getKey(), service.getValue());
serviceNodes.add(generateServiceTypeNode.execute());
}

var textDocument = TextDocuments.from(balTemplate);
var syntaxTree = SyntaxTree.from(textDocument);
ModulePartNode oldRoot = syntaxTree.rootNode();
ModulePartNode newRoot = oldRoot.modify().withMembers(oldRoot.members().addAll(serviceNodes)).apply();
var modifiedTree = syntaxTree.replaceNode(oldRoot, newRoot);

try {
var formattedSourceCode = Formatter.format(modifiedTree).toSourceCode();
logger.debug("Generated the source code for the service types: {}", formattedSourceCode);
} catch (FormatterException e) {
logger.error("Could not format the generated code, may be syntax issue in the generated code. " +
"Generated code: {}", modifiedTree.toSourceCode());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.asyncapi.codegenerator.usecase;

import io.apicurio.datamodels.asyncapi.models.AaiChannelItem;
import io.apicurio.datamodels.asyncapi.models.AaiDocument;
import io.apicurio.datamodels.asyncapi.models.AaiMessage;
import io.ballerina.asyncapi.codegenerator.configuration.BallerinaAsyncApiException;
import io.ballerina.asyncapi.codegenerator.configuration.Constants;
import io.ballerina.asyncapi.codegenerator.usecase.utils.CodegenUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExtractServiceTypesFromSpec implements UseCase {
private final AaiDocument asyncApiSpec;
private final CodegenUtils codegenUtils = new CodegenUtils();

public ExtractServiceTypesFromSpec(AaiDocument asyncApiSpec) {
this.asyncApiSpec = asyncApiSpec;
}

@Override
public Map<String, List<String>> execute() throws BallerinaAsyncApiException {
Map<String, List<String>> serviceTypes = new HashMap<>();
for (Map.Entry<String, AaiChannelItem> channel : asyncApiSpec.channels.entrySet()) {
List<String> remoteFunctions = new ArrayList<>();
String serviceType;
if (channel.getValue().getExtension(Constants.X_BALLERINA_SERVICE_TYPE) == null) {
serviceType = codegenUtils.getValidName(channel.getKey(), true);
} else {
serviceType = channel.getValue().getExtension(Constants.X_BALLERINA_SERVICE_TYPE).value.toString();
}
AaiMessage mainMessage = channel.getValue().subscribe.message;
if (mainMessage.oneOf != null) {
for(AaiMessage message: mainMessage.oneOf) {
if (message.getExtension(Constants.X_BALLERINA_EVENT_TYPE) == null) {
throw new BallerinaAsyncApiException(
"Could not find the ".concat(Constants.X_BALLERINA_EVENT_TYPE)
.concat(" attribute in the message of the channel ").concat(channel.getKey()));
}
remoteFunctions.add(
message.getExtension(Constants.X_BALLERINA_EVENT_TYPE).value.toString());
}
} else {
if (mainMessage.getExtension(Constants.X_BALLERINA_EVENT_TYPE) == null) {
throw new BallerinaAsyncApiException(
"Could not find the ".concat(Constants.X_BALLERINA_EVENT_TYPE)
.concat(" attribute in the message of the channel ").concat(channel.getKey()));
}
remoteFunctions.add(channel.getValue()
.subscribe.message.getExtension(Constants.X_BALLERINA_EVENT_TYPE).value.toString());
}
serviceTypes.put(serviceType, remoteFunctions);
}
return serviceTypes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@

import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.*;
import static io.ballerina.compiler.syntax.tree.NodeFactory.*;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.SEMICOLON_TOKEN;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.*;

public class GenerateRecordNode implements UseCase {
private final AaiDocument asyncApiSpec;
Expand All @@ -51,7 +50,6 @@ public GenerateRecordNode(AaiDocument asyncApiSpec, Map.Entry<String, AaiSchema>
public TypeDefinitionNode execute() throws BallerinaAsyncApiException {
var typeName = AbstractNodeFactory
.createIdentifierToken(codegenUtils.escapeIdentifier(recordFields.getKey().trim()));
Token typeKeyWord = AbstractNodeFactory.createIdentifierToken("public type");
TypeDefinitionNode typeDefinitionNode;
List<Node> schemaDoc = new ArrayList<>();
List<Node> recordFieldList = new ArrayList<>();
Expand All @@ -67,7 +65,7 @@ public TypeDefinitionNode execute() throws BallerinaAsyncApiException {
createMarkdownDocumentationNode(createNodeList(schemaDoc));
var metadataNode = createMetadataNode(documentationNode, createEmptyNodeList());
typeDefinitionNode = NodeFactory.createTypeDefinitionNode(metadataNode,
null, typeKeyWord, typeName, recordTypeDescriptorNode, createToken(SEMICOLON_TOKEN));
createToken(PUBLIC_KEYWORD), createToken(TYPE_KEYWORD), typeName, recordTypeDescriptorNode, createToken(SEMICOLON_TOKEN));
return typeDefinitionNode;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.asyncapi.codegenerator.usecase;

import io.ballerina.asyncapi.codegenerator.configuration.BallerinaAsyncApiException;
import io.ballerina.asyncapi.codegenerator.usecase.utils.CodegenUtils;
import io.ballerina.compiler.syntax.tree.*;

import java.util.ArrayList;
import java.util.List;

import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.*;
import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken;
import static io.ballerina.compiler.syntax.tree.NodeFactory.*;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.*;

public class GenerateServiceTypeNode implements UseCase {
private final String serviceTypeName;
private final List<String> remoteFunctionNames;
private final CodegenUtils codegenUtils = new CodegenUtils();

public GenerateServiceTypeNode(String serviceTypeName, List<String> remoteFunctionNames) {
this.serviceTypeName = serviceTypeName;
this.remoteFunctionNames = remoteFunctionNames;
}

@Override
public TypeDefinitionNode execute() throws BallerinaAsyncApiException {
List<Node> remoteFunctions = new ArrayList<>();
remoteFunctionNames.forEach(remoteFunction -> {
var methodDeclarationNode = createMethodDeclarationNode(
SyntaxKind.METHOD_DECLARATION, null, createNodeList(createToken(REMOTE_KEYWORD)),
createToken(SyntaxKind.FUNCTION_KEYWORD), createIdentifierToken(remoteFunction), createEmptyNodeList(),
createFunctionSignatureNode(
createToken(OPEN_PAREN_TOKEN), createSeparatedNodeList(),
createToken(CLOSE_PAREN_TOKEN), null),
createToken(SyntaxKind.SEMICOLON_TOKEN));
remoteFunctions.add(methodDeclarationNode);
});
var serviceTypeToken = AbstractNodeFactory
.createIdentifierToken(serviceTypeName);
var recordTypeDescriptorNode =
NodeFactory.createRecordTypeDescriptorNode(createIdentifierToken("service object"),
createToken(OPEN_BRACE_TOKEN), createNodeList(remoteFunctions), null,
createToken(SyntaxKind.CLOSE_BRACE_TOKEN));
return createTypeDefinitionNode(null, createToken(PUBLIC_KEYWORD),
createToken(TYPE_KEYWORD), serviceTypeToken, recordTypeDescriptorNode, createToken(SEMICOLON_TOKEN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public String escapeIdentifier(String identifier) {
* @param identifier input function name, record name or operation Id
* @return string with new generated name
*/
public String getValidName(String identifier, boolean isSchema) {
public String getValidName(String identifier, boolean capitalizeFirstChar) {
//For the flatten enable we need to remove first Part of valid name check
// this - > !identifier.matches("\\b[a-zA-Z][a-zA-Z0-9]*\\b") &&
if (!identifier.matches("\\b[0-9]*\\b")) {
Expand All @@ -85,7 +85,7 @@ public String getValidName(String identifier, boolean isSchema) {
}
identifier = validName.toString();
}
if (isSchema) {
if (capitalizeFirstChar) {
return identifier.substring(0, 1).toUpperCase(Locale.ENGLISH) + identifier.substring(1);
} else {
return identifier.substring(0, 1).toLowerCase(Locale.ENGLISH) + identifier.substring(1);
Expand Down
7 changes: 0 additions & 7 deletions src/main/resources/types.bal

This file was deleted.

0 comments on commit 23ca9e6

Please sign in to comment.