Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,18 @@ public void testServiceArtifactGenerationWithSimpleService() throws Exception {
try {
executeBallerinaCommand(projectDirPath, true);

Path endpointYaml = projectDirPath.resolve("target")
Path endpointsYaml = projectDirPath.resolve("target")
.resolve(ARTIFACT_DIR)
.resolve("service_endpoint.yaml");
.resolve("endpoints.yaml");
Path openAPIYaml = projectDirPath.resolve("target")
.resolve(ARTIFACT_DIR)
.resolve("service_openapi.yaml");
Assert.assertTrue(Files.exists(endpointYaml), "Endpoint YAML should be generated");
Assert.assertTrue(Files.exists(endpointsYaml), "Endpoints YAML should be generated");
Assert.assertTrue(Files.exists(openAPIYaml), "OpenAPI YAML should be generated");

Path expectedEndpointFile = RESOURCE_DIRECTORY.resolve("../yaml_files").resolve("service_endpoint.yaml");
Path expectedOpenAPIFile = RESOURCE_DIRECTORY.resolve("../yaml_files").resolve("service_openapi_1.yaml");

verifyYamlContent(endpointYaml, expectedEndpointFile);
Assert.assertTrue(Files.readString(endpointsYaml).contains("schemaPath: \"service_openapi.yaml\""));
verifyYamlContent(openAPIYaml, expectedOpenAPIFile);
} finally {
deleteDirectories(projectDirPath);
Expand Down Expand Up @@ -147,16 +146,16 @@ public void testServiceArtifactGenerationForMultipleServices() throws Exception
yamlFiles = paths.filter(path -> path.toString().endsWith(".yaml")).sorted().toList();
}

Assert.assertEquals(yamlFiles.size(), 10,
"Expected openapi and endpoint artifacts for all services");
Assert.assertEquals(yamlFiles.size(), 6,
"Expected OpenAPI artifacts for all services and one endpoint artifact");
Assert.assertEquals(yamlFiles.stream()
.map(this::safeFileName)
.filter(fileName -> fileName.contains("_openapi"))
.count(), 5L, "Expected 5 OpenAPI artifact files");
Assert.assertEquals(yamlFiles.stream()
.map(this::safeFileName)
.filter(fileName -> fileName.contains("_endpoint"))
.count(), 5L, "Expected 5 endpoint artifact files");
.filter("endpoints.yaml"::equals)
.count(), 1L, "Expected one endpoint artifact file");

} finally {
deleteDirectories(projectDirPath);
Expand All @@ -168,22 +167,21 @@ public void testEndpointYamlGenerationWithRegularServicePath() throws Exception
Path projectDirPath = RESOURCE_DIRECTORY.resolve("sample_package_48");
try {
executeBallerinaCommand(projectDirPath, true);
Path endpointYaml = projectDirPath.resolve("target")
Path endpointsYaml = projectDirPath.resolve("target")
.resolve(ARTIFACT_DIR)
.resolve("service_userservice_endpoint.yaml");
.resolve("endpoints.yaml");
Path openAPIYaml = projectDirPath.resolve("target")
.resolve(ARTIFACT_DIR)
.resolve("service_userservice_openapi.yaml");
Assert.assertTrue(Files.exists(endpointYaml),
"Endpoint YAML for regular base path should be generated");
Assert.assertTrue(Files.exists(endpointsYaml),
"Endpoints YAML for regular base path should be generated");
Assert.assertTrue(Files.exists(openAPIYaml),
"OpenAPI YAML for regular base path should be generated");

Path expectedEndpointFile = RESOURCE_DIRECTORY.resolve("../yaml_files")
.resolve("service_userservice_endpoint.yaml");
Path expectedOpenAPIFile = RESOURCE_DIRECTORY.resolve("../yaml_files")
.resolve("service_userservice_openapi.yaml");
verifyYamlContent(endpointYaml, expectedEndpointFile);
Assert.assertTrue(Files.readString(endpointsYaml)
.contains("schemaPath: \"service_userservice_openapi.yaml\""));
verifyYamlContent(openAPIYaml, expectedOpenAPIFile);
} finally {
deleteDirectories(projectDirPath);
Expand All @@ -209,8 +207,8 @@ public void testConfigurablePortWithDefaultValue() throws Exception {
try {
executeBallerinaCommand(projectDirPath, true);
Path artifactDir = projectDirPath.resolve("target").resolve(ARTIFACT_DIR);
Path endpointYaml = artifactDir.resolve("service_endpoint.yaml");
assertEndpointPort(endpointYaml, 8080);
Path endpointsYaml = artifactDir.resolve("endpoints.yaml");
assertEndpointPort(endpointsYaml, 8080);
} finally {
deleteDirectories(projectDirPath);
}
Expand All @@ -226,16 +224,8 @@ public void testEndpointYamlGenerationWithEmptyServicePath() throws Exception {
Path artifactDir = projectDirPath.resolve("target").resolve(ARTIFACT_DIR);
Assert.assertTrue(Files.exists(artifactDir), "Artifact directory should exist");

List<String> endpointFiles;
try (Stream<Path> paths = Files.walk(artifactDir)) {
endpointFiles = paths
.map(this::safeFileName)
.filter(fileName -> fileName.endsWith("_endpoint.yaml")).toList();
}

Assert.assertEquals(endpointFiles.size(), 1, "Expected exactly one endpoint YAML file");
Assert.assertTrue(endpointFiles.getFirst().matches("service_[0-9]+_endpoint\\.yaml"),
"Endpoint YAML file should use fallback hash-based naming for empty service path");
Assert.assertTrue(Files.exists(artifactDir.resolve("endpoints.yaml")),
"Expected consolidated endpoints YAML file");

} finally {
deleteDirectories(projectDirPath);
Expand Down Expand Up @@ -272,7 +262,7 @@ public void testInProcessServiceArtifactGenerationWithExportEndpoints() throws E
Assert.assertTrue(Files.exists(artifactDir), "Artifact directory should exist");
Assert.assertTrue(Files.exists(artifactDir.resolve("service_openapi.yaml")),
"OpenAPI artifact file should be generated");
Assert.assertTrue(Files.exists(artifactDir.resolve("service_endpoint.yaml")),
Assert.assertTrue(Files.exists(artifactDir.resolve("endpoints.yaml")),
"Endpoint artifact file should be generated");
} finally {
deleteDirectories(projectDirPath);
Expand Down Expand Up @@ -306,7 +296,7 @@ public void testEndpointYamlGeneratorWithMissingPortVariableReportsDiagnostic()

EndpointYamlGenerator generator = new EndpointYamlGenerator(contextData.serviceNode, context, server);
Endpoint endpoint = generator.getEndpoint();
generator.writeEndpointYaml();
generator.addEndpointArtifact();

Assert.assertEquals(endpoint.getPort(), 0,
"Endpoint port should fallback to 0 when no port variable default is provided");
Expand All @@ -333,7 +323,7 @@ public void testEndpointYamlGeneratorWithInvalidPortValue() throws IOException {

EndpointYamlGenerator generator = new EndpointYamlGenerator(contextData.serviceNode, context, server);
Endpoint endpoint = generator.getEndpoint();
generator.writeEndpointYaml();
generator.addEndpointArtifact();

Assert.assertEquals(endpoint.getPort(), 0,
"Endpoint port should remain default when server variable is non-numeric");
Expand Down Expand Up @@ -369,12 +359,11 @@ public void testExportServiceArtifact() throws Exception {
diagnostics);

Path artifactDir = projectDirPath.resolve("target").resolve(ARTIFACT_DIR);
Path endpointYaml = artifactDir.resolve("service_endpoint.yaml");
Path openapiYaml = artifactDir.resolve("service_openapi.yaml");
Assert.assertTrue(Files.exists(artifactDir),
"exportServiceArtifact must create target/artifact/");

Assert.assertTrue(Files.exists(endpointYaml));
Assert.assertEquals(project.endpointArtifacts().size(), 1);
Assert.assertTrue(Files.exists(openapiYaml));
deleteDirectories(projectDirPath);
}
Expand Down Expand Up @@ -812,6 +801,7 @@ private SyntaxNodeAnalysisContext createSyntaxNodeAnalysisContext(TestContextDat
}
yield null;
}
case "addEndpointArtifact" -> null;
default -> throw new UnsupportedOperationException("Unsupported context method: " +
method.getName());
});
Expand Down
3 changes: 0 additions & 3 deletions compiler-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ dependencies {

implementation group: 'io.ballerina.scan', name: 'scan-command', version: "${balScanVersion}"

compileOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}"
compileOnly "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonDatabindVersion}"

externalJars group: 'io.ballerina.openapi', name: 'ballerina-to-openapi', version: "${ballerinaToOpenApiVersion}"
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,18 @@

package io.ballerina.stdlib.http.compiler.endpointyaml.generator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import io.ballerina.compiler.syntax.tree.Node;
import io.ballerina.compiler.syntax.tree.NodeList;
import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode;
import io.ballerina.projects.Package;
import io.ballerina.projects.Project;
import io.ballerina.projects.plugins.EndpointArtifact;
import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext;
import io.ballerina.tools.diagnostics.DiagnosticFactory;
import io.ballerina.tools.diagnostics.DiagnosticInfo;
import io.ballerina.tools.diagnostics.DiagnosticSeverity;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.servers.ServerVariables;

import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static io.ballerina.openapi.service.mapper.utils.CodegenUtils.resolveContractFileName;

public class EndpointYamlGenerator {
private final ServiceDeclarationNode node;
Expand All @@ -49,19 +38,16 @@ public class EndpointYamlGenerator {

private static final PrintStream outStream = System.out;

private static final String ARTIFACT = "artifact";
private static final String REST = "REST";
private static final String YAML_EXTENSION = ".yaml";
private static final String OPENAPI_SUFFIX = "_openapi";
private static final String ENDPOINT_SUFFIX = "_endpoint";
private static final String PORT = "port";

private int portVal = 0;

private final Server server;

/*
* Generates the .yaml file with endpoint details of HTTP service
/**
* Adds endpoint details of HTTP service to the build context.
*/
public EndpointYamlGenerator(ServiceDeclarationNode node, SyntaxNodeAnalysisContext context, Server server) {
this.node = node;
Expand Down Expand Up @@ -98,12 +84,10 @@ public Endpoint getEndpoint() {
return new Endpoint(this.portVal, basePath, REST, this.schemaFileName);
}

public void writeEndpointYaml() throws IOException {
public void addEndpointArtifact() {
Endpoint ep = getEndpoint();
Path outPath = resolveOutputPath();
String fileName = buildEndpointFileName(outPath);
Path path = outPath.resolve(ARTIFACT).resolve(fileName + YAML_EXTENSION);
writeYaml(path, new EndpointWrapper(ep));
context.addEndpointArtifact(new EndpointArtifact(getEndpointName(), ep.getPort(), ep.getBasePath(),
ep.getType(), ep.getSchemaPath()));
}

private String getBasePath() {
Expand All @@ -115,31 +99,8 @@ private String getBasePath() {
return serviceBasePath.toString();
}

private Path resolveOutputPath() throws IOException {
Package currentPackage = this.context.currentPackage();
Project project = currentPackage.project();
Path outPath = project.targetDir();
Files.createDirectories(Paths.get(String.valueOf(outPath), ARTIFACT));
return outPath;
}

private String buildEndpointFileName(Path outPath) {
String base = schemaFileName.split("\\.")[0].replace(OPENAPI_SUFFIX, ENDPOINT_SUFFIX);
return resolveContractFileName(outPath.resolve(ARTIFACT), base, false);
}

private void writeYaml(Path path, EndpointWrapper wrapper) throws IOException {
YAMLFactory yamlFactory = YAMLFactory.builder()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
.build();
ObjectMapper mapper = new ObjectMapper(yamlFactory);
mapper.findAndRegisterModules();

try (Writer writer = Files.newBufferedWriter(path)) {
mapper.writeValue(writer, wrapper);
} catch (IOException e) {
throw new IOException("Failed to write endpoint yaml to " + path, e);
}
private String getEndpointName() {
return schemaFileName.split("\\.")[0].replace(OPENAPI_SUFFIX, "");
Comment on lines +102 to +103

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator/EndpointYamlGenerator.java'

echo '--- outline ---'
ast-grep outline "$file" --view expanded || true

echo
echo '--- relevant lines ---'
cat -n "$file" | sed -n '1,220p'

echo
echo '--- OPENAPI_SUFFIX references ---'
rg -n "OPENAPI_SUFFIX|getEndpointName|schemaFileName" compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator -S

Repository: ballerina-platform/module-ballerina-http

Length of output: 8067


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator/FileNameGeneratorUtil.java'

echo '--- outline ---'
ast-grep outline "$file" --view expanded || true

echo
echo '--- relevant lines ---'
cat -n "$file" | sed -n '1,220p'

echo
echo '--- related constants/usages ---'
rg -n "OPENAPI_SUFFIX|YAML_EXTENSION|getFileName|FileNameGeneratorUtil" compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator -S

Repository: ballerina-platform/module-ballerina-http

Length of output: 10413


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- getNormalizedFileName definition ---'
rg -n "String getNormalizedFileName|getNormalizedFileName\\(" -S compiler-plugin/src/main/java

echo
echo '--- endpoint artifact consumers ---'
rg -n "EndpointArtifact\\(|getEndpointName\\(|addEndpointArtifact\\(" -S compiler-plugin/src/main/java

Repository: ballerina-platform/module-ballerina-http

Length of output: 1370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- OpenAPISpecGenerator relevant section ---'
file='compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/OpenAPISpecGenerator.java'
ast-grep outline "$file" --view expanded || true
echo
cat -n "$file" | sed -n '130,210p'

echo
echo '--- search for replace(OPENAPI_SUFFIX, "") and suffix stripping patterns ---'
rg -n 'replace\(OPENAPI_SUFFIX, ""\)|endsWith\(OPENAPI_SUFFIX\)|substring\(0, .*OPENAPI_SUFFIX\.length\(\)\)' compiler-plugin/src/main/java -S

Repository: ballerina-platform/module-ballerina-http

Length of output: 6412


Strip only the trailing _openapi suffix

getEndpointName() uses replace(OPENAPI_SUFFIX, ""), so any _openapi earlier in the schema name is removed too. A service/path name containing that substring will produce the wrong endpoint name; trim only the trailing suffix instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator/EndpointYamlGenerator.java`
around lines 102 - 103, The endpoint name extraction in getEndpointName() is
removing every occurrence of OPENAPI_SUFFIX instead of only the trailing one,
which can corrupt names containing that substring earlier in the schema name.
Update getEndpointName() in EndpointYamlGenerator to trim only the suffix at the
end of schemaFileName after splitting off the extension, and keep the rest of
the name unchanged.

}

private static void reportMissingPortConfigDiagnostic(SyntaxNodeAnalysisContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
}

private void exportEndpointYaml(ServiceDeclarationNode serviceNode, SyntaxNodeAnalysisContext context,
OASResult oasResult, List<Diagnostic> diagnostics) {

Check warning on line 170 in compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/endpointyaml/generator/ServiceArtifactsExtractor.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused method parameter "diagnostics".

See more on https://sonarcloud.io/project/issues?id=ballerina-platform_module-ballerina-http&issues=AZ87MzHHrQuZpUZwJtgF&open=AZ87MzHHrQuZpUZwJtgF&pullRequest=2640
if (oasResult.getOpenAPI().isEmpty()) {
return;
}
Expand All @@ -179,12 +179,7 @@
for (Server server: servers) {
EndpointYamlGenerator endpointYamlGeneratorHttp =
new EndpointYamlGenerator(serviceNode, context, server);
try {
endpointYamlGeneratorHttp.writeEndpointYaml();
} catch (IOException e) {
diagnostics.add(getDiagnostics(
new ExceptionDiagnostic(DiagnosticMessages.OAS_CONVERTOR_108, e.toString())));
}
endpointYamlGeneratorHttp.addEndpointArtifact();
}

}
Expand Down
2 changes: 0 additions & 2 deletions compiler-plugin/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
requires io.swagger.v3.oas.models;
requires io.ballerina.openapi.service;
requires io.ballerina.scan;
requires com.fasterxml.jackson.dataformat.yaml;
requires com.fasterxml.jackson.databind;
requires java.logging;
requires io.ballerina.runtime;
}
Loading