Skip to content

Commit 5c75519

Browse files
committed
additional properties support
1 parent 0b4a401 commit 5c75519

7 files changed

Lines changed: 447 additions & 2 deletions

File tree

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ public CodegenModel fromModel(String name, Schema model) {
291291
}
292292
}
293293

294+
// Handle additionalProperties for models that have both properties and additionalProperties
295+
Schema addlProps = ModelUtils.getAdditionalProperties(model);
296+
if (addlProps != null && model.getProperties() != null && !model.getProperties().isEmpty()) {
297+
// This model has both defined properties AND additionalProperties
298+
codegenModel.additionalPropertiesType = getTypeDeclaration(addlProps);
299+
// Add import for web::json::value which is used to store additional properties
300+
codegenModel.imports.add("#include <cpprest/json.h>");
301+
}
302+
294303
return codegenModel;
295304
}
296305

@@ -386,6 +395,18 @@ private Schema resolveSchema(Schema schema) {
386395
* @return a string value used as the `dataType` field for model templates,
387396
* `returnType` for api templates
388397
*/
398+
/**
399+
* Check if a schema is a pure map (has additionalProperties but no defined properties).
400+
* Schemas with both properties and additionalProperties should be treated as models, not maps.
401+
*/
402+
private boolean isPureMapSchema(Schema schema) {
403+
if (!ModelUtils.isMapSchema(schema)) {
404+
return false;
405+
}
406+
// If the schema has defined properties, it's not a pure map
407+
return schema.getProperties() == null || schema.getProperties().isEmpty();
408+
}
409+
389410
@Override
390411
public String getTypeDeclaration(Schema p) {
391412
// Resolve the schema to check for nested maps/arrays - refs that point to map schemas
@@ -394,7 +415,8 @@ public String getTypeDeclaration(Schema p) {
394415
if (ModelUtils.isArraySchema(resolved)) {
395416
Schema inner = ModelUtils.getSchemaItems(resolved);
396417
return getSchemaType(p) + "<" + getTypeDeclaration(inner) + ">";
397-
} else if (ModelUtils.isMapSchema(resolved)) {
418+
} else if (isPureMapSchema(resolved)) {
419+
// Only treat as map if it has additionalProperties but NO defined properties
398420
Schema inner = ModelUtils.getAdditionalProperties(resolved);
399421
return getSchemaType(p) + "<utility::string_t, " + getTypeDeclaration(inner) + ">";
400422
}
@@ -435,7 +457,7 @@ public String toDefaultValue(Schema p) {
435457
return "0L";
436458
}
437459
return "0";
438-
} else if (ModelUtils.isMapSchema(p)) {
460+
} else if (isPureMapSchema(p)) {
439461
String inner = getSchemaType(ModelUtils.getAdditionalProperties(p));
440462
return "std::map<utility::string_t, " + inner + ">()";
441463
} else if (ModelUtils.isArraySchema(p)) {

modules/openapi-generator/src/main/resources/cpp-rest-sdk-client/model-header.mustache

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,23 @@ public:
261261
{{/isInherited}}
262262

263263
{{/vars}}
264+
{{#additionalPropertiesType}}
265+
266+
/// <summary>
267+
/// Get additional properties (properties not defined in the schema)
268+
/// </summary>
269+
std::map<utility::string_t, web::json::value> getAdditionalProperties() const;
270+
bool additionalPropertiesIsSet() const;
271+
void unsetAdditionalProperties();
272+
/// <summary>
273+
/// Set additional properties
274+
/// </summary>
275+
void setAdditionalProperties(const std::map<utility::string_t, web::json::value>& value);
276+
/// <summary>
277+
/// Add a single additional property
278+
/// </summary>
279+
void addAdditionalProperty(const utility::string_t& key, const web::json::value& value);
280+
{{/additionalPropertiesType}}
264281

265282
protected:
266283
{{#vars}}
@@ -285,6 +302,10 @@ protected:
285302
{{/isInherited}}
286303

287304
{{/vars}}
305+
{{#additionalPropertiesType}}
306+
std::map<utility::string_t, web::json::value> m_AdditionalProperties;
307+
bool m_AdditionalPropertiesIsSet;
308+
{{/additionalPropertiesType}}
288309
};
289310

290311
{{/isEnum}}

modules/openapi-generator/src/main/resources/cpp-rest-sdk-client/model-source.mustache

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ void {{classname}}::setValue({{classname}}::e{{classname}} const value)
207207
{{/isNullable}}
208208
{{/isInherited}}
209209
{{/vars}}
210+
{{#additionalPropertiesType}}
211+
m_AdditionalPropertiesIsSet = false;
212+
{{/additionalPropertiesType}}
210213
}
211214

212215
{{classname}}::~{{classname}}()
@@ -262,6 +265,16 @@ web::json::value {{classname}}::toJson() const
262265
{{/isNullable}}
263266
{{/isInherited}}
264267
{{/vars}}
268+
{{#additionalPropertiesType}}
269+
// Serialize additional properties
270+
if(m_AdditionalPropertiesIsSet)
271+
{
272+
for(const auto& item : m_AdditionalProperties)
273+
{
274+
val[item.first] = item.second;
275+
}
276+
}
277+
{{/additionalPropertiesType}}
265278

266279
return val;
267280
}
@@ -295,6 +308,24 @@ bool {{classname}}::fromJson(const web::json::value& val)
295308
}
296309
{{/isInherited}}
297310
{{/vars}}
311+
{{#additionalPropertiesType}}
312+
// Capture additional properties (keys not defined in the schema)
313+
if(val.is_object())
314+
{
315+
for(const auto& item : val.as_object())
316+
{
317+
// Skip known properties
318+
{{#vars}}
319+
{{^isInherited}}
320+
if(item.first == utility::conversions::to_string_t(_XPLATSTR("{{baseName}}"))) continue;
321+
{{/isInherited}}
322+
{{/vars}}
323+
// This is an additional property
324+
m_AdditionalProperties[item.first] = item.second;
325+
m_AdditionalPropertiesIsSet = true;
326+
}
327+
}
328+
{{/additionalPropertiesType}}
298329
return ok;
299330
}
300331

@@ -515,6 +546,36 @@ void {{classname}}::unset{{name}}()
515546
{{/isNullable}}
516547
}
517548
{{/isInherited}}{{/vars}}
549+
{{#additionalPropertiesType}}
550+
551+
std::map<utility::string_t, web::json::value> {{classname}}::getAdditionalProperties() const
552+
{
553+
return m_AdditionalProperties;
554+
}
555+
556+
void {{classname}}::setAdditionalProperties(const std::map<utility::string_t, web::json::value>& value)
557+
{
558+
m_AdditionalProperties = value;
559+
m_AdditionalPropertiesIsSet = true;
560+
}
561+
562+
void {{classname}}::addAdditionalProperty(const utility::string_t& key, const web::json::value& value)
563+
{
564+
m_AdditionalProperties[key] = value;
565+
m_AdditionalPropertiesIsSet = true;
566+
}
567+
568+
bool {{classname}}::additionalPropertiesIsSet() const
569+
{
570+
return m_AdditionalPropertiesIsSet;
571+
}
572+
573+
void {{classname}}::unsetAdditionalProperties()
574+
{
575+
m_AdditionalProperties.clear();
576+
m_AdditionalPropertiesIsSet = false;
577+
}
578+
{{/additionalPropertiesType}}
518579
{{/isEnum}}
519580
{{/oneOf}}
520581
{{#modelNamespaceDeclarations}}

modules/openapi-generator/src/test/resources/3_0/cpp-restsdk/petstore.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,4 +896,14 @@ components:
896896
healthRecords:
897897
description: Nested map - category to (date to record)
898898
$ref: '#/components/schemas/NestedStringMap'
899+
SchemaWithPropsAndAddlProps:
900+
description: Object with both defined properties and additionalProperties
901+
type: object
902+
nullable: true
903+
properties:
904+
propA:
905+
type: string
906+
propB:
907+
type: string
908+
additionalProperties: {}
899909

samples/client/petstore/cpp-restsdk/client/.openapi-generator/FILES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ include/CppRestPetstoreClient/model/Order.h
2525
include/CppRestPetstoreClient/model/Page.h
2626
include/CppRestPetstoreClient/model/Pet.h
2727
include/CppRestPetstoreClient/model/PetStatistics.h
28+
include/CppRestPetstoreClient/model/SchemaWithPropsAndAddlProps.h
2829
include/CppRestPetstoreClient/model/SchemaWithSet.h
2930
include/CppRestPetstoreClient/model/SchemaWithSet_vaccinationBook.h
3031
include/CppRestPetstoreClient/model/Tag.h
@@ -51,6 +52,7 @@ src/model/Order.cpp
5152
src/model/Page.cpp
5253
src/model/Pet.cpp
5354
src/model/PetStatistics.cpp
55+
src/model/SchemaWithPropsAndAddlProps.cpp
5456
src/model/SchemaWithSet.cpp
5557
src/model/SchemaWithSet_vaccinationBook.cpp
5658
src/model/Tag.cpp
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* OpenAPI Petstore
3+
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
4+
*
5+
* The version of the OpenAPI document: 1.0.0
6+
*
7+
* NOTE: This class is auto generated by OpenAPI-Generator 7.19.0-SNAPSHOT.
8+
* https://openapi-generator.tech
9+
* Do not edit the class manually.
10+
*/
11+
12+
/*
13+
* SchemaWithPropsAndAddlProps.h
14+
*
15+
* Object with both defined properties and additionalProperties
16+
*/
17+
18+
#ifndef ORG_OPENAPITOOLS_CLIENT_MODEL_SchemaWithPropsAndAddlProps_H_
19+
#define ORG_OPENAPITOOLS_CLIENT_MODEL_SchemaWithPropsAndAddlProps_H_
20+
21+
#include <boost/optional.hpp>
22+
23+
#include "CppRestPetstoreClient/ModelBase.h"
24+
25+
#include <cpprest/details/basic_types.h>
26+
#include <map>
27+
#include <cpprest/json.h>
28+
#include "CppRestPetstoreClient/AnyType.h"
29+
30+
namespace org {
31+
namespace openapitools {
32+
namespace client {
33+
namespace model {
34+
35+
36+
37+
/// <summary>
38+
/// Object with both defined properties and additionalProperties
39+
/// </summary>
40+
class SchemaWithPropsAndAddlProps
41+
: public ModelBase
42+
{
43+
public:
44+
SchemaWithPropsAndAddlProps();
45+
virtual ~SchemaWithPropsAndAddlProps();
46+
47+
/////////////////////////////////////////////
48+
/// ModelBase overrides
49+
50+
void validate() override;
51+
52+
web::json::value toJson() const override;
53+
bool fromJson(const web::json::value& json) override;
54+
55+
void toMultipart(std::shared_ptr<MultipartFormData> multipart, const utility::string_t& namePrefix) const override;
56+
bool fromMultiPart(std::shared_ptr<MultipartFormData> multipart, const utility::string_t& namePrefix) override;
57+
58+
59+
/////////////////////////////////////////////
60+
/// SchemaWithPropsAndAddlProps members
61+
62+
63+
utility::string_t getPropA() const;
64+
bool propAIsSet() const;
65+
void unsetPropA();
66+
void setPropA(const utility::string_t& value);
67+
68+
utility::string_t getPropB() const;
69+
bool propBIsSet() const;
70+
void unsetPropB();
71+
void setPropB(const utility::string_t& value);
72+
73+
74+
/// <summary>
75+
/// Get additional properties (properties not defined in the schema)
76+
/// </summary>
77+
std::map<utility::string_t, web::json::value> getAdditionalProperties() const;
78+
bool additionalPropertiesIsSet() const;
79+
void unsetAdditionalProperties();
80+
/// <summary>
81+
/// Set additional properties
82+
/// </summary>
83+
void setAdditionalProperties(const std::map<utility::string_t, web::json::value>& value);
84+
/// <summary>
85+
/// Add a single additional property
86+
/// </summary>
87+
void addAdditionalProperty(const utility::string_t& key, const web::json::value& value);
88+
89+
protected:
90+
utility::string_t m_PropA;
91+
bool m_PropAIsSet;
92+
93+
utility::string_t m_PropB;
94+
bool m_PropBIsSet;
95+
96+
std::map<utility::string_t, web::json::value> m_AdditionalProperties;
97+
bool m_AdditionalPropertiesIsSet;
98+
};
99+
100+
101+
}
102+
}
103+
}
104+
}
105+
106+
#endif /* ORG_OPENAPITOOLS_CLIENT_MODEL_SchemaWithPropsAndAddlProps_H_ */

0 commit comments

Comments
 (0)