Skip to content

Commit 67df039

Browse files
authored
[cpp-httplib-server] Generate and compile a spec without models (#25279)
A spec with no schemas never reaches postProcessAllModels, so the model namespace stayed unset and postProcessOperationsWithModels threw a NullPointerException. Default it in processOpts with the other namespaces. The API source then still named the undeclared model namespace and relied on a model header for nlohmann/json. Include the JSON header directly and emit the using-directive only when the API uses a model. Fixes #24854 Co-authored-by: ybai08 <271969568+ybai08@users.noreply.github.com>
1 parent da984d8 commit 67df039

11 files changed

Lines changed: 112 additions & 6 deletions

File tree

‎modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppHttplibServerCodegen.java‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
944944
ArrayList<String> sortedModels = new ArrayList<>(modelsUsed);
945945
Collections.sort(sortedModels);
946946
objs.put("modelsUsed", new ArrayList<>(sortedModels));
947+
objs.put("hasModelsUsed", true);
947948
objs.put("includeVariantHeader", INCLUDE_VARIANT);
948949
}
949950
if (includeOptionalHeader) {
@@ -1038,12 +1039,8 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
10381039

10391040
Map<String, ModelsMap> processed = super.postProcessAllModels(objs);
10401041

1041-
// Compute modelNamespace ONCE and append className
1042+
// Compute modelNamespace ONCE (defaulted in processOpts) and append className
10421043
String modelNamespaceBase = (String) additionalProperties.get(MODEL_NAMESPACE);
1043-
if (modelNamespaceBase == null || modelNamespaceBase.isEmpty()) {
1044-
modelNamespaceBase = MODEL_SUFFIX.toLowerCase(Locale.ROOT);
1045-
additionalProperties.put(MODEL_NAMESPACE, modelNamespaceBase);
1046-
}
10471044

10481045
for (Map.Entry<String, ModelsMap> entry : processed.entrySet()) {
10491046
ModelsMap modelsMap = entry.getValue();
@@ -2143,6 +2140,14 @@ public void processOpts() {
21432140
}
21442141
additionalProperties.put(API_NAMESPACE, apiNamespaceValue);
21452142

2143+
// Set model namespace here rather than in postProcessAllModels, which is
2144+
// never called for a spec without models; the operations still read it.
2145+
String modelNamespaceValue = (String) additionalProperties.get(MODEL_NAMESPACE);
2146+
if (modelNamespaceValue == null || modelNamespaceValue.isEmpty()) {
2147+
modelNamespaceValue = MODEL_SUFFIX.toLowerCase(Locale.ROOT);
2148+
}
2149+
additionalProperties.put(MODEL_NAMESPACE, modelNamespaceValue);
2150+
21462151
// Handle addApiImplStubs option
21472152
if (additionalProperties.containsKey(ADD_API_IMPL_STUBS)) {
21482153
boolean addApiImplStubs = Boolean.parseBoolean(additionalProperties.get(ADD_API_IMPL_STUBS).toString());

‎modules/openapi-generator/src/main/resources/cpp-httplib-server/api-source.mustache‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
// System headers
44
#include <algorithm>
55
#include <cctype>
6-
#include <locale>{{#operations}}{{#operation}}{{#vendorExtensions}}{{#hasAnyRequestSchema}}{{#hasArrayParams}}{{#-first}}
6+
#include <locale>
7+
#include <nlohmann/json.hpp>{{#operations}}{{#operation}}{{#vendorExtensions}}{{#hasAnyRequestSchema}}{{#hasArrayParams}}{{#-first}}
78
#include <sstream>{{/-first}}{{/hasArrayParams}}{{/hasAnyRequestSchema}}{{/vendorExtensions}}{{/operation}}{{/operations}}
89

910
// Project headers
@@ -16,8 +17,10 @@ constexpr int {{constName}} = {{statusCode}};
1617

1718
namespace {{apiNamespace}} {
1819
20+
{{#hasModelsUsed}}
1921
using namespace {{modelNamespace}};
2022

23+
{{/hasModelsUsed}}
2124
{{#operations}}
2225
{{#operation}}
2326
{{#vendorExtensions}}

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/cpphttplibserver/CppHttplibServerCodegenTest.java‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@
1717

1818
import static org.openapitools.codegen.utils.StringUtils.underscore;
1919

20+
import java.io.File;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.List;
24+
2025
import org.openapitools.codegen.*;
26+
import org.openapitools.codegen.config.CodegenConfigurator;
2127
import org.openapitools.codegen.languages.CppHttplibServerCodegen;
2228
import org.testng.Assert;
2329
import org.testng.annotations.Test;
@@ -144,4 +150,63 @@ public void testApiNamespaceHandling() {
144150

145151
Assert.assertEquals(codegen.additionalProperties().get("apiNamespace"), "myapp::api");
146152
}
153+
154+
@Test
155+
public void testDefaultModelNamespaceIsSetBeforeAnyModelIsProcessed() {
156+
CppHttplibServerCodegen fresh = new CppHttplibServerCodegen();
157+
fresh.processOpts();
158+
159+
Assert.assertEquals(fresh.additionalProperties().get("modelNamespace"), "models");
160+
}
161+
162+
@Test
163+
public void testSpecWithoutModelsGeneratesPrimitiveResponses() throws Exception {
164+
// #24854: a spec with no schemas never reaches postProcessAllModels
165+
File output = Files.createTempDirectory("test").toFile();
166+
output.deleteOnExit();
167+
168+
final CodegenConfigurator configurator = new CodegenConfigurator()
169+
.setGeneratorName("cpp-httplib-server")
170+
.setInputSpec("src/test/resources/3_1/cpp-httplib-server/primitive-responses-no-models.yaml")
171+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
172+
173+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
174+
files.forEach(File::deleteOnExit);
175+
176+
Path apiHeader = files.stream()
177+
.map(File::toPath)
178+
.filter(path -> path.toString().endsWith("Api.h"))
179+
.findFirst()
180+
.orElseThrow(() -> new AssertionError("no API header generated: " + files));
181+
TestUtils.assertFileContains(apiHeader,
182+
"using TextGetResponse = std::string;",
183+
"using FileGetResponse = std::string;");
184+
// Without a model header the source must include the JSON library itself
185+
// and must not name the (undeclared) model namespace.
186+
Path apiSource = Path.of(apiHeader.toString().replaceAll("\\.h$", ".cpp"));
187+
TestUtils.assertFileContains(apiSource, "#include <nlohmann/json.hpp>");
188+
TestUtils.assertFileNotContains(apiSource, "using namespace models;");
189+
for (File file : files) {
190+
if (file.getName().endsWith(".h") || file.getName().endsWith(".cpp")) {
191+
TestUtils.assertFileNotContains(file.toPath(), "String.h", "File.h");
192+
}
193+
}
194+
}
195+
196+
@Test
197+
public void testApiSourceUsesModelNamespaceWhenModelsAreUsed() throws Exception {
198+
File output = Files.createTempDirectory("test").toFile();
199+
output.deleteOnExit();
200+
201+
final CodegenConfigurator configurator = new CodegenConfigurator()
202+
.setGeneratorName("cpp-httplib-server")
203+
.setInputSpec("src/test/resources/3_0/cpp-httplib-server/petstore.json")
204+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
205+
206+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
207+
files.forEach(File::deleteOnExit);
208+
209+
Path petApiSource = output.toPath().resolve("api/PetApi.cpp");
210+
TestUtils.assertFileContains(petApiSource, "using namespace models;", "#include <nlohmann/json.hpp>");
211+
}
147212
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
openapi: 3.1.0
2+
info:
3+
title: Primitive Response Test
4+
version: 1.0.0
5+
paths:
6+
/text:
7+
get:
8+
operationId: getText
9+
responses:
10+
'200':
11+
description: Successful response
12+
content:
13+
text/plain:
14+
schema:
15+
type: string
16+
/file:
17+
get:
18+
operationId: getFile
19+
responses:
20+
'200':
21+
description: Binary response
22+
content:
23+
application/octet-stream:
24+
schema:
25+
type: string
26+
format: binary

‎samples/server/petstore/cpp-httplib-server/feature-test/api/CompositionApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "CompositionApi.h"

‎samples/server/petstore/cpp-httplib-server/feature-test/api/DatatypesApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "DatatypesApi.h"

‎samples/server/petstore/cpp-httplib-server/feature-test/api/ParametersApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "ParametersApi.h"

‎samples/server/petstore/cpp-httplib-server/feature-test/api/SecurityApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "SecurityApi.h"

‎samples/server/petstore/cpp-httplib-server/petstore/api/PetApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "PetApi.h"

‎samples/server/petstore/cpp-httplib-server/petstore/api/StoreApi.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <cctype>
1010
#include <locale>
11+
#include <nlohmann/json.hpp>
1112

1213
// Project headers
1314
#include "StoreApi.h"

0 commit comments

Comments
 (0)