Skip to content
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
38 changes: 38 additions & 0 deletions modules/core/src/alloy/openapi/OpenApiConfigExtension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* Copyright 2022 Disney Streaming
*
* Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://disneystreaming.github.io/TOST-1.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package alloy.openapi;

public final class OpenApiConfigExtension {


// OpenAPI 3.1 supports multiple examples, but Swagger UI do not (see https://github.com/swagger-api/swagger-ui/issues/10503), so this feature is hidden by default.
private boolean enableMultipleExamples = false;

public void setEnableMultipleExamples(boolean enableMultipleExamples) {
this.enableMultipleExamples = enableMultipleExamples;
}

public boolean getEnableMultipleExamples() {
return this.enableMultipleExamples;
}

@Override
public String toString() {
return "OpenApiConfigExtension{" +
"enableMultipleExamples=" + enableMultipleExamples +
'}';
}
}
62 changes: 46 additions & 16 deletions modules/openapi/src/alloy/openapi/DataExamplesMapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import scala.jdk.CollectionConverters._
import alloy.DataExamplesTrait
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.model.node.Node
import software.amazon.smithy.openapi.OpenApiConfig
import software.amazon.smithy.openapi.OpenApiVersion
import scala.math.Ordering.Implicits._

class DataExamplesMapper() extends JsonSchemaMapper {

Expand All @@ -32,25 +35,52 @@ class DataExamplesMapper() extends JsonSchemaMapper {
schemaBuilder: Builder,
config: JsonSchemaConfig
): Builder = if (shape.hasTrait(classOf[DataExamplesTrait])) {
shape
val examples = shape
.getTrait(classOf[DataExamplesTrait])
.get
.getExamples()
.asScala
.headOption match {
case Some(example)
if example.getExampleType != DataExamplesTrait.DataExampleType.STRING =>
schemaBuilder.putExtension("example", example.getContent())
case Some(example)
if example.getExampleType == DataExamplesTrait.DataExampleType.STRING =>
val maybeStrNode = example.getContent().asStringNode()
val res = if (maybeStrNode.isPresent) {
Node.parse(maybeStrNode.get.getValue)
} else {
ObjectNode.builder().build()
}
schemaBuilder.putExtension("example", res)
case _ => schemaBuilder
}
.toList
implicit val ordering: Ordering[OpenApiVersion] =
Ordering.fromLessThan[OpenApiVersion]((a, b) => a.compareTo(b) < 0)
val configExtension = config.getExtensions(classOf[OpenApiConfigExtension])
if (examples.isEmpty)
schemaBuilder
else
config match {
case openApiConfig: OpenApiConfig
if openApiConfig.getVersion >= OpenApiVersion.VERSION_3_1_0 && configExtension.getEnableMultipleExamples =>
putMultipleExamples(examples, schemaBuilder)
case _ =>
putSingleExample(examples.head, schemaBuilder)
}
} else schemaBuilder

private def convertExample(example: DataExamplesTrait.DataExample) = {
if (example.getExampleType == DataExamplesTrait.DataExampleType.STRING) {
val maybeStrNode = example.getContent().asStringNode()
if (maybeStrNode.isPresent) {
Node.parse(maybeStrNode.get.getValue)
} else {
ObjectNode.builder().build()
}
} else {
example.getContent()
}
}

private def putSingleExample(
example: DataExamplesTrait.DataExample,
schemaBuilder: Builder
) =
schemaBuilder.putExtension("example", convertExample(example))

private def putMultipleExamples(
examples: List[DataExamplesTrait.DataExample],
schemaBuilder: Builder
) = {
val array = Node.arrayNode(examples.map(convertExample) *)
schemaBuilder.putExtension("examples", array)
}

}
4 changes: 2 additions & 2 deletions modules/openapi/test/resources/bar.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"operationId": "BarOp",
"responses": {
"200": {
"description": "BarOp200response",
"description": "BarOp 200 response",
"content": {
"application/json": {
"schema": {
Expand Down Expand Up @@ -64,4 +64,4 @@
}
}
}
}
}
38 changes: 19 additions & 19 deletions modules/openapi/test/resources/foo.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"operationId": "GetUnion",
"responses": {
"200": {
"description": "GetUnion200response",
"description": "GetUnion 200 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -20,7 +20,7 @@
}
},
"500": {
"description": "GeneralServerError500response",
"description": "GeneralServerError 500 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -36,7 +36,7 @@
"get": {
"summary": "A simple greeting operation",
"externalDocs": {
"description": "APIHomepage2",
"description": "API Homepage 2",
"url": "https://www.example2.com/"
},
"operationId": "Greet",
Expand Down Expand Up @@ -108,7 +108,7 @@
],
"responses": {
"200": {
"description": "Greet200response",
"description": "Greet 200 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -118,7 +118,7 @@
}
},
"404": {
"description": "404Response",
"description": "404 Response",
"headers": {
"x-error-one": {
"schema": {
Expand All @@ -129,7 +129,7 @@
}
},
"500": {
"description": "GeneralServerError500response",
"description": "GeneralServerError 500 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -153,17 +153,17 @@
"examples": {
"ONE": {
"value": {
"in": "testinput"
"in": "test input"
}
},
"THREE": {
"value": {
"in": "testinputthree"
"in": "test input three"
}
},
"TWO": {
"value": {
"in": "testinputtwo"
"in": "test input two"
}
}
}
Expand All @@ -173,7 +173,7 @@
},
"responses": {
"200": {
"description": "TestErrorsInExamples200response",
"description": "TestErrorsInExamples 200 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -182,15 +182,15 @@
"examples": {
"ONE": {
"value": {
"out": "testoutput"
"out": "test output"
}
}
}
}
}
},
"404": {
"description": "404Response",
"description": "404 Response",
"headers": {
"x-one": {
"schema": {
Expand Down Expand Up @@ -223,20 +223,20 @@
"examples": {
"THREE": {
"value": {
"messageTwo": "Notfoundmessagetwo"
"messageTwo": "Not found message two"
}
},
"TWO": {
"value": {
"message": "Notfoundmessage"
"message": "Not found message"
}
}
}
}
}
},
"500": {
"description": "GeneralServerError500response",
"description": "GeneralServerError 500 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -253,7 +253,7 @@
"operationId": "GetValues",
"responses": {
"200": {
"description": "GetValues200response",
"description": "GetValues 200 response",
"content": {
"application/json": {
"schema": {
Expand All @@ -263,7 +263,7 @@
}
},
"500": {
"description": "GeneralServerError500response",
"description": "GeneralServerError 500 response",
"content": {
"application/json": {
"schema": {
Expand Down Expand Up @@ -694,7 +694,7 @@
}
},
"externalDocs": {
"description": "APIHomepage",
"description": "API Homepage",
"url": "https://www.example.com/"
}
}
}
6 changes: 6 additions & 0 deletions modules/openapi/test/resources/foo.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ union DoubleOrFloat {
@dataExamples([
{
smithy: { name: "Meow" }
},
{
smithy: { name: "Miau" }
}
])
structure Cat {
Expand All @@ -208,6 +211,9 @@ structure Cat {
@dataExamples([
{
json: { name: "Woof" }
},
{
json: { name: "Hau hau" }
}
])
structure Dog {
Expand Down
Loading