Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openapi-generator): Suppress faulty additionalProperties: true #689

Merged
merged 12 commits into from
Jan 29, 2025
1 change: 1 addition & 0 deletions datamodel/openapi/openapi-api-sample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
<pojoConstructorVisibility>protected</pojoConstructorVisibility>
<useOneOfInterfaces>true</useOneOfInterfaces>
<enumUnknownDefaultCase>true</enumUnknownDefaultCase>
<stopAdditionalProperties>true</stopAdditionalProperties>
MatKuhr marked this conversation as resolved.
Show resolved Hide resolved
</additionalProperties>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ components:
example: 123
Order:
type: object
additionalProperties: true
required:
- productId
- quantity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination;
import com.sap.cloud.sdk.datamodel.openapi.sample.model.Order;
import com.sap.cloud.sdk.datamodel.openapi.sample.model.SodaWithId;

@WireMockTest
Expand Down Expand Up @@ -48,7 +49,7 @@ void testPutPayload()
}

@Test
void testJacksonSerialization()
void testJacksonSerializeSodaWithId()
throws JsonProcessingException
{
expected = """
Expand All @@ -75,6 +76,25 @@ void testJacksonSerialization()
assertThat(new ObjectMapper().writeValueAsString(obj)).isEqualToIgnoringWhitespace(expected);
}

@Test
void testJacksonSerializeOrder()
newtork marked this conversation as resolved.
Show resolved Hide resolved
throws JsonProcessingException
{
expected = """
{
"productId": 100,
"quantity": 5,
"totalPrice": 6.0,
"typelessProperty":null,
"nullableProperty":null,
"shoesize": 44
}
""";
final Order order = Order.create().productId(100L).quantity(5).totalPrice(6.0f);
order.setCustomField("shoesize", 44);
assertThat(new ObjectMapper().writeValueAsString(order)).isEqualToIgnoringWhitespace(expected);
Copy link
Contributor Author

@newtork newtork Jan 10, 2025

Choose a reason for hiding this comment

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

(Comment)

Without the enabled feature toggle this test assertion fails:

Expecting actual:
  "{}"
to be equal to:
  "{
  "productId": 100,
  "quantity": 5,
  "totalPrice": 6.0,
  "packaging" : "bottle",
  "shoesize": 44
}

}

private void verify( String requestBody )
{
WireMock
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.sap.cloud.sdk.datamodel.openapi.generator;

import static com.sap.cloud.sdk.datamodel.openapi.generator.GeneratorCustomProperties.STOP_ADDITIONAL_PROPERTIES;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Year;
Expand All @@ -11,6 +13,7 @@

import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.languages.JavaClientCodegen;
Expand All @@ -23,6 +26,7 @@

import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import io.swagger.v3.parser.core.models.ParseOptions;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -55,7 +59,7 @@ static ClientOptInput convertGenerationConfiguration(
setGlobalSettings(generationConfiguration);
final var inputSpecFile = inputSpec.toString();

final var config = createCodegenConfig();
final var config = createCodegenConfig(generationConfiguration);
config.setOutputDir(generationConfiguration.getOutputDirectory());
config.setLibrary(LIBRARY_NAME);
config.setApiPackage(generationConfiguration.getApiPackage());
Expand All @@ -69,7 +73,7 @@ static ClientOptInput convertGenerationConfiguration(
return clientOptInput;
}

private static JavaClientCodegen createCodegenConfig()
private static JavaClientCodegen createCodegenConfig( @Nonnull final GenerationConfiguration config )
{
return new JavaClientCodegen()
{
Expand All @@ -89,6 +93,16 @@ public OperationsMap postProcessOperationsWithModels(
}
return super.postProcessOperationsWithModels(ops, allModels);
}

@SuppressWarnings( { "rawtypes", "RedundantSuppression" } )
@Override
protected void updateModelForObject( CodegenModel m, Schema schema )
{
if( STOP_ADDITIONAL_PROPERTIES.isEnabled(config) ) {
schema.setAdditionalProperties(Boolean.FALSE);
}
super.updateModelForObject(m, schema);
}
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.sap.cloud.sdk.datamodel.openapi.generator;

import javax.annotation.Nonnull;

import com.sap.cloud.sdk.datamodel.openapi.generator.model.GenerationConfiguration;

import lombok.RequiredArgsConstructor;

/**
* Optional feature toggles, may be used internally only.
*/
@RequiredArgsConstructor
enum GeneratorCustomProperties
{
/**
* Disable additional properties in generated models. They resolve to model classes extending from HashMap,
* effectively disabling serialization. Jackson by default only serializes the map entries and will ignore all
* fields from the model class.
*/
STOP_ADDITIONAL_PROPERTIES("stopAdditionalProperties", "false");
newtork marked this conversation as resolved.
Show resolved Hide resolved

private final String key;
private final String defaultValue;

/**
* Check if the feature is enabled.
*
* @param config
* The generation configuration.
* @return True if the feature is enabled, false otherwise.
*/
public boolean isEnabled( @Nonnull final GenerationConfiguration config )
{
final var value = getValue(config);
return !value.isEmpty() && !"false".equalsIgnoreCase(value.trim());
}

/**
* Get the value of the feature.
*
* @param config
* The generation configuration.
* @return The value of the feature.
*/
@Nonnull
public String getValue( @Nonnull final GenerationConfiguration config )
{
return config.getAdditionalProperties().getOrDefault(key, defaultValue);
}
}
Loading