Skip to content

Commit

Permalink
Add error message for openapi spec generations when bal service has c…
Browse files Browse the repository at this point in the history
…ompilation issue
  • Loading branch information
lnash94 committed May 26, 2023
1 parent 5eb744e commit 12dffbc
Show file tree
Hide file tree
Showing 16 changed files with 308 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.ballerina.tools.diagnostics.DiagnosticSeverity;

import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -68,6 +69,15 @@
* @since 2.0.0
*/
public class HttpServiceAnalysisTask implements AnalysisTask<SyntaxNodeAnalysisContext> {
static boolean isWarningPrinted = false;

static void setIsWarningPrinted(boolean isWarningPrinted) {
HttpServiceAnalysisTask.isWarningPrinted = isWarningPrinted;
}

// public void setWarning(boolean isPrinted) {
// setIsWarningPrinted(isPrinted);
// }

@Override
public void perform(SyntaxNodeAnalysisContext context) {
Expand All @@ -83,8 +93,15 @@ public void perform(SyntaxNodeAnalysisContext context) {
boolean hasErrors = context.compilation().diagnosticResult()
.diagnostics().stream()
.anyMatch(d -> DiagnosticSeverity.ERROR.equals(d.diagnosticInfo().severity()));

if (hasErrors) {
// if there are any compilation errors, do not proceed
if (!isWarningPrinted) {
setIsWarningPrinted(true);
PrintStream outStream = System.out;
outStream.println("OpenAPI contract generation failed due to Ballerina package has compilation" +
" error.");
}
return;
}
Path outPath = project.targetDir();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void generateOAS3DefinitionsAllService(Path servicePath, Path outPath, St
.anyMatch(d -> DiagnosticSeverity.ERROR.equals(d.diagnosticInfo().severity()));
if (hasCompilationErrors || hasErrorsFromCodeGenAndModify) {
// if there are any compilation errors, do not proceed and those diagnostic will display to user
outStream.println("OpenAPI contract generation failed due to Ballerina code has compilation error/s. : \n");
outStream.println("OpenAPI contract generation failed due to Ballerina code has compilation errors. :");
compilation.diagnosticResult().diagnostics().forEach(diagnostic -> {
outStream.println(diagnostic.toString());
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package io.ballerina.openapi;

import org.apache.commons.lang3.StringUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
Expand All @@ -30,9 +31,14 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.ballerina.openapi.TestUtil.OUT;
import static io.ballerina.openapi.TestUtil.TEST_DISTRIBUTION_PATH;
import static io.ballerina.openapi.extension.build.BuildExtensionTests.DISTRIBUTION_FILE_NAME;

/**
* This is abstract class for containing the main common test methods for the other subclasses.
*/
Expand Down Expand Up @@ -95,4 +101,17 @@ public void compareGeneratedSyntaxTreewithExpectedSyntaxTree(String generated, S
expectedBallerinaContent = (expectedBallerinaContent.trim()).replaceAll("\\s+", "");
Assert.assertTrue(generatedFile.contains(expectedBallerinaContent));
}

public Process getProcess(List<String> buildArgs, Path testResourcePath) throws IOException {
String balFile = "bal";
if (System.getProperty("os.name").startsWith("Windows")) {
balFile = "bal.bat";
}
buildArgs.add(0, TEST_DISTRIBUTION_PATH.resolve(DISTRIBUTION_FILE_NAME).resolve("bin")
.resolve(balFile).toString());
OUT.println("Executing: " + StringUtils.join(buildArgs, ' '));
ProcessBuilder pb = new ProcessBuilder(buildArgs);
pb.directory(testResourcePath.toFile());
return pb.start();
}
}
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.openapi.cmd;

import io.ballerina.openapi.OpenAPITest;
import io.ballerina.openapi.TestUtil;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;

import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
import static io.ballerina.openapi.TestUtil.assertOnErrorStream;

/**
* This {@code BallerinaToOpenAPITests} contains all the ballerina to openapi command negative.
*/
public class BallerinaToOpenAPICLINegativeTests extends OpenAPITest {
public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/ballerina_sources");

@BeforeClass
public void setupDistributions() throws IOException {
TestUtil.cleanDistribution();
}

@Test(description = "Test for given bal service file contains compilation issues")
public void constraintWithUnsupportedStringPattern() throws IOException, InterruptedException {
String balFilePath = "bal_service_has_compilation_issue/service.bal";
List<String> buildArgs = new LinkedList<>();
buildArgs.add(0, "openapi");
buildArgs.add("-i");
buildArgs.add(balFilePath);
buildArgs.add("-o");
buildArgs.add(tmpDir.toString());

Process process = getProcess(buildArgs, TEST_RESOURCE);

String out = "OpenAPI contract generation failed due to Ballerina code has compilation errors. :\n" +
"ERROR [main.bal:(11:1,11:2)] invalid token '}'";
//Thread for wait out put generate
Thread.sleep(5000);
// compare generated file has not included constraint annotation for scenario record field.
Assert.assertFalse(Files.exists(TEST_RESOURCE.resolve("payload_openapi.yaml")));
process.waitFor();
assertOnErrorStream(process, out);
}

@AfterClass
public void cleanUp() throws IOException {
TestUtil.cleanDistribution();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
*/
package io.ballerina.openapi.cmd;

import io.ballerina.openapi.OpenAPITest;
import io.ballerina.openapi.TestUtil;
import org.apache.commons.lang3.StringUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
Expand All @@ -35,12 +37,15 @@
import java.util.stream.Stream;

import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
import static io.ballerina.openapi.TestUtil.OUT;
import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
import static io.ballerina.openapi.TestUtil.TEST_DISTRIBUTION_PATH;
import static io.ballerina.openapi.TestUtil.assertOnErrorStream;

/**
* This {@code BallerinaToOpenAPITests} contains all the ballerina to openapi command with compiler annotation.
*/
public class BallerinaToOpenAPITests {
public class BallerinaToOpenAPITests extends OpenAPITest {
public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/ballerina_sources");

Expand Down Expand Up @@ -144,25 +149,26 @@ public void nonOpenAPIAnnotationWithWithoutBasePath() throws IOException, Interr
"project_non_openapi_annotation_without_base_path/result.yaml");
}

@Test(description = "Test for given bal service file contains compilation issues")
public void constraintWithUnsupportedStringPattern() throws IOException, InterruptedException {
String balFilePath = "bal_service_has_compilation_issue/service.bal";
Process process = getProcessForOpenAPISpecGeneration(balFilePath);

String out = "OpenAPI contract generation failed due to Ballerina code has compilation errors. :\n" +
"ERROR [main.bal:(11:1,11:2)] invalid token '}'";
//Thread for wait out put generate
Thread.sleep(5000);
// compare generated file has not included constraint annotation for scenario record field.
Assert.assertFalse(Files.exists(TEST_RESOURCE.resolve("payload_openapi.yaml")));
process.waitFor();
assertOnErrorStream(process, out);
}

@AfterClass
public void cleanUp() throws IOException {
TestUtil.cleanDistribution();
}

//Replace contract file path in generated service file with common URL.
public String replaceContractPath(Stream<String> expectedServiceLines, String expectedService,
String generatedService) {

Pattern pattern = Pattern.compile("\\bcontract\\b: \"(.*?)\"");
Matcher matcher = pattern.matcher(generatedService);
matcher.find();
String contractPath = "contract: " + "\"" + matcher.group(1) + "\"";
expectedService = expectedService.replaceAll("\\bcontract\\b: \"(.*?)\"",
Matcher.quoteReplacement(contractPath));
expectedServiceLines.close();
return expectedService;
}

private String getStringFromGivenBalFile(Path expectedServiceFile) throws IOException {
Stream<String> expectedServiceLines = Files.lines(expectedServiceFile);
String expectedServiceContent = expectedServiceLines.collect(Collectors.joining(System.lineSeparator()));
Expand All @@ -181,4 +187,25 @@ private void executeCommand(String resourcePath, String generatedFile, String ex
String expectedYaml = getStringFromGivenBalFile(TEST_RESOURCE.resolve(expectedPath));
Assert.assertEquals(expectedYaml, generatedOpenAPI);
}

public Process getProcessForOpenAPISpecGeneration(String ballerinaFilePath) throws IOException {
List<String> buildArgs = new LinkedList<>();
buildArgs.add(0, "openapi");
buildArgs.add("-i");
buildArgs.add(ballerinaFilePath);
buildArgs.add("-o");
buildArgs.add(tmpDir.toString());

String balFile = "bal";

if (System.getProperty("os.name").startsWith("Windows")) {
balFile = "bal.bat";
}
buildArgs.add(0, TEST_DISTRIBUTION_PATH.resolve(DISTRIBUTION_FILE_NAME).resolve("bin")
.resolve(balFile).toString());
OUT.println("Executing: " + StringUtils.join(buildArgs, ' '));
ProcessBuilder pb = new ProcessBuilder(buildArgs);
pb.directory(TEST_RESOURCE.toFile());
return pb.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.openapi.extension.build;

import io.ballerina.openapi.OpenAPITest;
import io.ballerina.openapi.TestUtil;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;

import static io.ballerina.openapi.TestUtil.DISTRIBUTIONS_DIR;
import static io.ballerina.openapi.TestUtil.RESOURCE;
import static io.ballerina.openapi.TestUtil.RESOURCES_PATH;
import static io.ballerina.openapi.TestUtil.assertOnErrorStream;

/**
* These negative tests are related to the `--export-openapi` flag for `bal build` command.
*/
public class BuildExtensionNegativeTests extends OpenAPITest {
public static final String DISTRIBUTION_FILE_NAME = DISTRIBUTIONS_DIR.toString();
public static final Path TEST_RESOURCE = Paths.get(RESOURCES_PATH.toString() + "/build");

@BeforeClass
public void setupDistributions() throws IOException {
TestUtil.cleanDistribution();
}

@Test(description = "Ballerina package has compilation error")
public void packageHasCompilationErrors() throws IOException, InterruptedException {
List<String> buildArgs = new LinkedList<>();
buildArgs.add("build");
buildArgs.add("--export-openapi");
Process process = getProcess(buildArgs, TEST_RESOURCE);

String out = "OpenAPI contract generation failed due to Ballerina code has compilation errors. :\n" +
"ERROR [main.bal:(11:1,11:2)] invalid token '}'";
//Thread for wait out put generate
Thread.sleep(5000);
// compare generated file has not included constraint annotation for scenario record field.
Assert.assertFalse(Files.exists(RESOURCE.resolve("\"build/project_9/target/openapi/")));
process.waitFor();
assertOnErrorStream(process, out);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public void basePathWithUnicodeCharacter() throws IOException, InterruptedExcept
TEST_RESOURCE.resolve("project_9"), buildArgs);
Assert.assertTrue(Files.exists(RESOURCE.resolve("build/project_9/target/openapi/ชื่อ_openapi.yaml")));
}

private void executeCommand(String resourcePath) throws IOException, InterruptedException {
List<String> buildArgs = new LinkedList<>();
boolean successful = executeBuild(DISTRIBUTION_FILE_NAME,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
org = "openapi_gen_test"
name = "compilation_issue"
version = "0.1.0"

[build-options]
observabilityIncluded = true

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
openapi: 3.0.1
info:
title: Mock Yaml
version: 2.0.0
servers:
- url: "{server}:{port}/"
variables:
server:
default: http://localhost
port:
default: "9090"
paths:
/greeting:
get:
operationId: getGreeting
responses:
"200":
description: Ok
content:
text/plain:
schema:
type: string
components: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ballerina/http;

service /payload on new http:Listener(0) {
resource function get hello() {
}

resource function post path(@http:Payload string name) {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
org = "openapi_extension_test"
name = "compilation_issue"
version = "0.1.0"

[build-options]
observabilityIncluded = true

Loading

0 comments on commit 12dffbc

Please sign in to comment.