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

Add config for inline JSON Schema maps #2449

Merged
merged 1 commit into from
Nov 11, 2024
Merged
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 @@ -804,6 +804,26 @@ mapStrategy (``string``)
}
}

.. _generate-openapi-jsonschema-setting-useInlineMaps:

useInlineMaps (``boolean``)
Configures Smithy to generate ``map`` shapes inline instead of as
references. This is necessary for some code generators to distinguish
between ``maps`` and ``structure`` shapes when generating.

.. code-block:: json
:caption: smithy-build.json

{
"version": "1.0",
"plugins": {
"openapi": {
"service": "example.weather#Weather",
"useInlineMaps": true
}
}
}

.. _generate-openapi-jsonschema-setting-schemaDocumentExtensions:

schemaDocumentExtensions (``Map<String, any>``)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ public boolean isInlined(Shape shape) {
return true;
}

// Maps are not inlined by default, but can be if configured.
// Maps are usually not a generated type in programming languages,
// but JSON schema represents them as "object" types which code
// generators may not distinguish from converted structures.
//
// Some code generators, however, will treat inline "object" types
// as maps and referenced ones as structures.
if (shape.isMapShape() && config.getUseInlineMaps()) {
return true;
}

// Strings with the enum trait are never inlined. This helps to ensure
// that the name of an enum string can be round-tripped from
// Smithy -> JSON Schema -> Smithy, helps OpenAPI code generators to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public String toString() {
private boolean disableDefaultValues = false;
private boolean disableIntEnums = false;
private boolean addReferenceDescriptions = false;
private boolean useInlineMaps = false;

public JsonSchemaConfig() {
nodeMapper.setWhenMissingSetter(NodeMapper.WhenMissing.IGNORE);
Expand Down Expand Up @@ -479,4 +480,26 @@ public boolean getAddReferenceDescriptions() {
public void setAddReferenceDescriptions(boolean addReferenceDescriptions) {
this.addReferenceDescriptions = addReferenceDescriptions;
}

/**
* Whether to inline map shapes when creating JSON Schema object types
* from them.
*
* <p>Defaults to {@code false}.</p>
*
* @return Whether to inline map shapes in the resulting schema
*/
public boolean getUseInlineMaps() {
return useInlineMaps;
}

/**
* Sets whether to inline map shapes when creating JSON schema object types
* from them.
*
* @param useInlineMaps Whether to inline map shapes in the resulting schema.
*/
public void setUseInlineMaps(boolean useInlineMaps) {
this.useInlineMaps = useInlineMaps;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,32 @@ public void supportsMapPropertyNames() {
assertThat(schema.getAdditionalProperties().get().getType().get(), equalTo("string"));
}

@Test
public void supportsInlineMapPropertyNames() {
ShapeId shapeId = ShapeId.from("smithy.api#String");
StringShape string = StringShape.builder().id(shapeId).build();
MapShape map = MapShape.builder().id("a.b#Map").key(shapeId).value(shapeId).build();
StructureShape container = StructureShape.builder().id("a.b#Container").addMember("map", map.getId()).build();
Model model = Model.builder().addShapes(container, map, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setUseInlineMaps(true);
SchemaDocument document = JsonSchemaConverter.builder()
.config(config)
.model(model)
.build()
.convertShape(container);
Schema schema = document.getRootSchema();

assertThat(schema.getProperties().containsKey("map"), equalTo(true));
Schema mapMember = schema.getProperties().get("map");
assertThat(mapMember.getRef().isPresent(), equalTo(false));
assertThat(mapMember.getType().get(), equalTo("object"));
assertTrue(mapMember.getPropertyNames().isPresent());
assertThat(mapMember.getPropertyNames().get().getType().get(), equalTo("string"));
assertTrue(mapMember.getAdditionalProperties().isPresent());
assertThat(mapMember.getAdditionalProperties().get().getType().get(), equalTo("string"));
}

@Test
public void supportsMapPatternProperties() {
ShapeId shapeId = ShapeId.from("smithy.api#String");
Expand All @@ -596,6 +622,35 @@ public void supportsMapPatternProperties() {
assertThat(schema.getPatternProperties().get(pattern).getType().get(), equalTo("string"));
}

@Test
public void supportsInlineMapPatternProperties() {
ShapeId shapeId = ShapeId.from("smithy.api#String");
StringShape string = StringShape.builder().id(shapeId).build();
String pattern = "[a-z]{1,16}";
StringShape key = StringShape.builder().id("a.b#Key")
.addTrait(new PatternTrait(pattern)).build();
MapShape map = MapShape.builder().id("a.b#Map").key(key.getId()).value(shapeId).build();
StructureShape container = StructureShape.builder().id("a.b#Container").addMember("map", map.getId()).build();
Model model = Model.builder().addShapes(container, map, key, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setUseInlineMaps(true);
config.setMapStrategy(JsonSchemaConfig.MapStrategy.PATTERN_PROPERTIES);
SchemaDocument document = JsonSchemaConverter.builder()
.config(config)
.model(model)
.build()
.convertShape(container);
Schema schema = document.getRootSchema();

assertThat(schema.getProperties().containsKey("map"), equalTo(true));
Schema mapMember = schema.getProperties().get("map");
assertThat(mapMember.getRef().isPresent(), equalTo(false));
assertThat(mapMember.getType().get(), equalTo("object"));
assertThat(mapMember.getPatternProperties().size(), equalTo(1));
assertTrue(mapMember.getPatternProperties().containsKey(pattern));
assertThat(mapMember.getPatternProperties().get(pattern).getType().get(), equalTo("string"));
}

@Test
public void supportsMapPatternPropertiesWithDefaultPattern() {
ShapeId shapeId = ShapeId.from("smithy.api#String");
Expand Down
Loading