diff --git a/docs/source-2.0/guides/model-translations/converting-to-openapi.rst b/docs/source-2.0/guides/model-translations/converting-to-openapi.rst index 91746b39431..bbeb9a93c86 100644 --- a/docs/source-2.0/guides/model-translations/converting-to-openapi.rst +++ b/docs/source-2.0/guides/model-translations/converting-to-openapi.rst @@ -1550,6 +1550,29 @@ additionalAllowedCorsHeaders (``[string]``) } } + +.. _generate-openapi-apigateway-setting-syncCorsPreflightIntegration: + +syncCorsPreflightIntegration (``boolean``) + Set to true to sync CORS preflight integration request templates with all possible content-types + from other methods within the same path resource. + + .. code-block:: json + :caption: smithy-build.json + + { + "version": "1.0", + "plugins": { + "openapi": { + "service": "example.weather#Weather", + "syncCorsPreflightIntegration": true + } + } + } + + With this enabled, the `integration's passthroughBehavior`_ for CORS preflight integration + will be set to ``never``. + Binary types ============ @@ -1648,6 +1671,10 @@ additions during the OpenAPI conversion: * Adds static CORS response headers to API Gateway "gateway" responses. These are added only when no gateway responses are defined in the OpenAPI model. +.. note:: + If :ref:`syncCorsPreflightIntegration ` is + set to ``true``, the CORS preflight `integration's passthroughBehavior`_ will be set to ``never`` and the integration's + request templates will be synced with all possible content-types from other methods within the same path resource. .. _authorizers: @@ -2037,4 +2064,5 @@ The conversion process is highly extensible through .. _Lambda authorizers: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-authorizer.html .. _API Gateway's API key usage plans: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html .. _OpenAPI specification extension: https://spec.openapis.org/oas/v3.1.0#specification-extensions +.. _integration's passthroughBehavior: https://docs.aws.amazon.com/apigateway/latest/developerguide/integration-passthrough-behaviors.html .. _gradle installed: https://gradle.org/install/ diff --git a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsPreflightIntegration.java b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsPreflightIntegration.java index ad2eab0f11b..c80458cc43e 100644 --- a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsPreflightIntegration.java +++ b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsPreflightIntegration.java @@ -31,6 +31,7 @@ import software.amazon.smithy.jsonschema.Schema; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.knowledge.TopDownIndex; +import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.node.ObjectNode; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.traits.CorsTrait; @@ -45,6 +46,7 @@ import software.amazon.smithy.openapi.model.ResponseObject; import software.amazon.smithy.utils.CaseUtils; import software.amazon.smithy.utils.ListUtils; +import software.amazon.smithy.utils.SetUtils; /** * Adds CORS-preflight OPTIONS requests using mock API Gateway integrations. @@ -70,6 +72,7 @@ final class AddCorsPreflightIntegration implements ApiGatewayMapper { private static final Logger LOGGER = Logger.getLogger(AddCorsPreflightIntegration.class.getName()); private static final String API_GATEWAY_DEFAULT_ACCEPT_VALUE = "application/json"; private static final String INTEGRATION_EXTENSION = "x-amazon-apigateway-integration"; + private static final String REQUEST_TEMPLATES_KEY = "requestTemplates"; private static final String PREFLIGHT_SUCCESS = "{\"statusCode\":200}"; @Override @@ -95,7 +98,7 @@ private static PathItem addPreflightIntegration( LOGGER.fine(() -> "Adding CORS-preflight OPTIONS request and API Gateway integration for " + path); Map headers = deduceCorsHeaders(context, path, pathItem, corsTrait); return pathItem.toBuilder() - .options(createPreflightOperation(path, pathItem, headers)) + .options(createPreflightOperation(context, path, pathItem, headers)) .build(); } @@ -170,7 +173,7 @@ private static String getAllowMethods(PathItem item) { } private static OperationObject createPreflightOperation( - String path, PathItem pathItem, Map headers) { + Context context, String path, PathItem pathItem, Map headers) { return OperationObject.builder() .tags(ListUtils.of("CORS")) .security(Collections.emptyList()) @@ -178,7 +181,7 @@ private static OperationObject createPreflightOperation( .operationId(createOperationId(path)) .putResponse("200", createPreflightResponse(headers)) .parameters(findPathParameters(pathItem)) - .putExtension(INTEGRATION_EXTENSION, createPreflightIntegration(headers, pathItem)) + .putExtension(INTEGRATION_EXTENSION, createPreflightIntegration(context, headers, pathItem)) .build(); } @@ -214,7 +217,8 @@ private static ResponseObject createPreflightResponse(Map he return builder.build(); } - private static ObjectNode createPreflightIntegration(Map headers, PathItem pathItem) { + private static ObjectNode createPreflightIntegration( + Context context, Map headers, PathItem pathItem) { IntegrationResponse.Builder responseBuilder = IntegrationResponse.builder().statusCode("200"); // Add each CORS header to the mock integration response. @@ -222,13 +226,34 @@ private static ObjectNode createPreflightIntegration(Map hea responseBuilder.putResponseParameter("method.response.header." + e.getKey(), "'" + e.getValue() + "'"); } + boolean isPreflightSynced = Boolean.TRUE.equals(context.getConfig().getSyncCorsPreflightIntegration()); MockIntegrationTrait.Builder integration = MockIntegrationTrait.builder() // See https://forums.aws.amazon.com/thread.jspa?threadID=256140 .contentHandling("CONVERT_TO_TEXT") - .passThroughBehavior("when_no_match") + // Passthrough behavior "never" will fail the request with unsupported content type more appropriately. + // https://docs.aws.amazon.com/apigateway/latest/developerguide/integration-passthrough-behaviors.html + .passThroughBehavior(isPreflightSynced ? "never" : "when_no_match") .putResponse("default", responseBuilder.build()) .putRequestTemplate(API_GATEWAY_DEFAULT_ACCEPT_VALUE, PREFLIGHT_SUCCESS); + if (isPreflightSynced) { + // Adds request template for every unique Content-Type supported by all path operations. + // This ensures that for Content-Type(s) other than 'application/json', the entire request payload + // is not sent to APIGW mock integration as stipulated by 'when_no_match' passthroughBehavior. + // APIGW throws an error if the mock integration request does not follow a set contract, + // example {"statusCode":200}. + for (OperationObject operation : pathItem.getOperations().values()) { + ObjectNode extensionNode = operation.getExtension(INTEGRATION_EXTENSION) + .flatMap(Node::asObjectNode) + .orElse(Node.objectNode()); + Set mimeTypes = extensionNode.getObjectMember(REQUEST_TEMPLATES_KEY) + .map(ObjectNode::getStringMap) + .map(Map::keySet) + .orElse(SetUtils.of()); + mimeTypes.forEach(mimeType -> integration.putRequestTemplate(mimeType, PREFLIGHT_SUCCESS)); + } + } + // Add a request template for every mime-type of every response. for (OperationObject operation : pathItem.getOperations().values()) { for (ResponseObject response : operation.getResponses().values()) { diff --git a/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/CorsTest.java b/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/CorsTest.java index dd4caa02778..1cd507ecd23 100644 --- a/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/CorsTest.java +++ b/smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/CorsTest.java @@ -112,4 +112,37 @@ public OpenApi after(Context context, OpenApi openapi) { Node.assertEquals(result, expectedNode); } + + @Test + public void withPreflightIntegrationSync() { + Model model = Model.assembler(getClass().getClassLoader()) + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("cors-with-multi-request-templates.json")) + .assemble() + .unwrap(); + OpenApiConfig config = new OpenApiConfig(); + config.setService(ShapeId.from("example.smithy#MyService")); + config.setSyncCorsPreflightIntegration(true); + ObjectNode result = OpenApiConverter.create().config(config).convertToNode(model); + Node expectedNode = Node.parse(IoUtils.toUtf8String( + getClass().getResourceAsStream("cors-with-preflight-sync.openapi.json"))); + + Node.assertEquals(result, expectedNode); + } + + @Test + public void withoutPreflightIntegrationSync() { + Model model = Model.assembler(getClass().getClassLoader()) + .discoverModels(getClass().getClassLoader()) + .addImport(getClass().getResource("cors-with-multi-request-templates.json")) + .assemble() + .unwrap(); + OpenApiConfig config = new OpenApiConfig(); + config.setService(ShapeId.from("example.smithy#MyService")); + ObjectNode result = OpenApiConverter.create().config(config).convertToNode(model); + Node expectedNode = Node.parse(IoUtils.toUtf8String( + getClass().getResourceAsStream("cors-without-preflight-sync.openapi.json"))); + + Node.assertEquals(result, expectedNode); + } } diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-multi-request-templates.json b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-multi-request-templates.json new file mode 100644 index 00000000000..714d3df7df0 --- /dev/null +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-multi-request-templates.json @@ -0,0 +1,102 @@ +{ + "smithy": "2.0", + "shapes": { + "example.smithy#MyService": { + "type": "service", + "version": "2006-03-01", + "operations": [ + { + "target": "example.smithy#MockPut" + }, + { + "target": "example.smithy#MockGet" + } + ], + "traits": { + "aws.protocols#restJson1": {}, + "aws.auth#sigv4": { + "name": "myservice" + }, + "smithy.api#cors": { + "origin": "https://www.example.com", + "maxAge": 86400, + "additionalAllowedHeaders": [ + "X-Service-Input-Metadata" + ], + "additionalExposedHeaders": [ + "X-Service-Output-Metadata" + ] + } + } + }, + "example.smithy#MockGet": { + "type": "operation", + "output": { + "target": "example.smithy#MockOutput" + }, + "traits": { + "aws.apigateway#mockIntegration": { + "passThroughBehavior": "never", + "requestTemplates": { + "application/json": "{\"statusCode\": 200}", + "application/x-www-form-urlencoded": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + } + } + } + }, + "smithy.api#http": { + "code": 200, + "method": "GET", + "uri": "/mock" + }, + "smithy.api#readonly": {} + } + }, + "example.smithy#MockPut": { + "type": "operation", + "output": { + "target": "example.smithy#MockOutput" + }, + "traits": { + "aws.apigateway#mockIntegration": { + "passThroughBehavior": "never", + "requestTemplates": { + "text/plain": "{\"statusCode\": 200}", + "application/xml": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + } + } + } + }, + "smithy.api#http": { + "code": 201, + "method": "PUT", + "uri": "/mock" + }, + "smithy.api#idempotent": {} + } + }, + "example.smithy#MockOutput": { + "type": "structure", + "members": { + "extendedRequestId": { + "target": "smithy.api#String", + "traits": { + "smithy.api#required": {} + } + } + } + } + } +} diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-preflight-sync.openapi.json b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-preflight-sync.openapi.json new file mode 100644 index 00000000000..5f3b8154ba1 --- /dev/null +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-preflight-sync.openapi.json @@ -0,0 +1,222 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "MyService", + "version": "2006-03-01" + }, + "paths": { + "/mock": { + "get": { + "operationId": "MockGet", + "responses": { + "200": { + "description": "MockGet 200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Expose-Headers": { + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MockGetResponseContent" + } + } + } + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}", + "application/x-www-form-urlencoded": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Expose-Headers": "'Content-Length,Content-Type,X-Amzn-Errortype,X-Amzn-Requestid,X-Service-Output-Metadata'" + } + } + }, + "type": "mock", + "passthroughBehavior": "never" + } + }, + "options": { + "description": "Handles CORS-preflight requests", + "operationId": "CorsMock", + "responses": { + "200": { + "description": "Canned response for CORS-preflight requests", + "headers": { + "Access-Control-Allow-Headers": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Max-Age": { + "schema": { + "type": "string" + } + } + } + } + }, + "security": [], + "tags": [ + "CORS" + ], + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "requestTemplates": { + "application/xml": "{\"statusCode\":200}", + "application/x-www-form-urlencoded": "{\"statusCode\":200}", + "application/json": "{\"statusCode\":200}", + "text/plain": "{\"statusCode\":200}" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Max-Age": "'86400'", + "method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Date,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-Service-Input-Metadata'", + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Allow-Methods": "'GET,PUT'" + }, + "statusCode": "200" + } + }, + "type": "mock", + "passthroughBehavior": "never" + } + }, + "put": { + "operationId": "MockPut", + "responses": { + "201": { + "description": "MockPut 201 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Expose-Headers": { + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MockPutResponseContent" + } + } + } + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "text/plain": "{\"statusCode\": 200}", + "application/xml": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Expose-Headers": "'Content-Length,Content-Type,X-Amzn-Errortype,X-Amzn-Requestid,X-Service-Output-Metadata'" + } + } + }, + "type": "mock", + "passthroughBehavior": "never" + } + } + } + }, + "components": { + "schemas": { + "MockGetResponseContent": { + "type": "object", + "properties": { + "extendedRequestId": { + "type": "string" + } + }, + "required": [ + "extendedRequestId" + ] + }, + "MockPutResponseContent": { + "type": "object", + "properties": { + "extendedRequestId": { + "type": "string" + } + }, + "required": [ + "extendedRequestId" + ] + } + }, + "securitySchemes": { + "aws.auth.sigv4": { + "type": "apiKey", + "description": "AWS Signature Version 4 authentication", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "security": [ + { + "aws.auth.sigv4": [ ] + } + ], + "x-amazon-apigateway-gateway-responses": { + "DEFAULT_4XX": { + "responseTemplates": { + "application/json": "{\"message\":$context.error.messageString}" + }, + "responseParameters": { + "gatewayresponse.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "gatewayresponse.header.Access-Control-Expose-Headers": "'X-Service-Output-Metadata'" + } + }, + "DEFAULT_5XX": { + "responseTemplates": { + "application/json": "{\"message\":$context.error.messageString}" + }, + "responseParameters": { + "gatewayresponse.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "gatewayresponse.header.Access-Control-Expose-Headers": "'X-Service-Output-Metadata'" + } + } + } +} diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-without-preflight-sync.openapi.json b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-without-preflight-sync.openapi.json new file mode 100644 index 00000000000..2ce6e10fa06 --- /dev/null +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-without-preflight-sync.openapi.json @@ -0,0 +1,219 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "MyService", + "version": "2006-03-01" + }, + "paths": { + "/mock": { + "get": { + "operationId": "MockGet", + "responses": { + "200": { + "description": "MockGet 200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Expose-Headers": { + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MockGetResponseContent" + } + } + } + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\"statusCode\": 200}", + "application/x-www-form-urlencoded": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Expose-Headers": "'Content-Length,Content-Type,X-Amzn-Errortype,X-Amzn-Requestid,X-Service-Output-Metadata'" + } + } + }, + "type": "mock", + "passthroughBehavior": "never" + } + }, + "options": { + "description": "Handles CORS-preflight requests", + "operationId": "CorsMock", + "responses": { + "200": { + "description": "Canned response for CORS-preflight requests", + "headers": { + "Access-Control-Allow-Headers": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Methods": { + "schema": { + "type": "string" + } + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Max-Age": { + "schema": { + "type": "string" + } + } + } + } + }, + "security": [], + "tags": [ + "CORS" + ], + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "requestTemplates": { + "application/json": "{\"statusCode\":200}" + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Max-Age": "'86400'", + "method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Date,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-Service-Input-Metadata'", + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Allow-Methods": "'GET,PUT'" + }, + "statusCode": "200" + } + }, + "type": "mock", + "passthroughBehavior": "when_no_match" + } + }, + "put": { + "operationId": "MockPut", + "responses": { + "201": { + "description": "MockPut 201 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + }, + "Access-Control-Expose-Headers": { + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MockPutResponseContent" + } + } + } + } + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "text/plain": "{\"statusCode\": 200}", + "application/xml": "{\"statusCode\": 200}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseTemplates": { + "application/json": "{\"extendedRequestId\": \"$context.extendedRequestId\"}" + }, + "responseParameters": { + "method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "method.response.header.Access-Control-Expose-Headers": "'Content-Length,Content-Type,X-Amzn-Errortype,X-Amzn-Requestid,X-Service-Output-Metadata'" + } + } + }, + "type": "mock", + "passthroughBehavior": "never" + } + } + } + }, + "components": { + "schemas": { + "MockGetResponseContent": { + "type": "object", + "properties": { + "extendedRequestId": { + "type": "string" + } + }, + "required": [ + "extendedRequestId" + ] + }, + "MockPutResponseContent": { + "type": "object", + "properties": { + "extendedRequestId": { + "type": "string" + } + }, + "required": [ + "extendedRequestId" + ] + } + }, + "securitySchemes": { + "aws.auth.sigv4": { + "type": "apiKey", + "description": "AWS Signature Version 4 authentication", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "awsSigv4" + } + } + }, + "security": [ + { + "aws.auth.sigv4": [ ] + } + ], + "x-amazon-apigateway-gateway-responses": { + "DEFAULT_4XX": { + "responseTemplates": { + "application/json": "{\"message\":$context.error.messageString}" + }, + "responseParameters": { + "gatewayresponse.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "gatewayresponse.header.Access-Control-Expose-Headers": "'X-Service-Output-Metadata'" + } + }, + "DEFAULT_5XX": { + "responseTemplates": { + "application/json": "{\"message\":$context.error.messageString}" + }, + "responseParameters": { + "gatewayresponse.header.Access-Control-Allow-Origin": "'https://www.example.com'", + "gatewayresponse.header.Access-Control-Expose-Headers": "'X-Service-Output-Metadata'" + } + } + } +} diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java index cf0db189498..90b83e14ddf 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/OpenApiConfig.java @@ -101,6 +101,7 @@ public String toString() { private List externalDocs = ListUtils.of( "Homepage", "API Reference", "User Guide", "Developer Guide", "Reference", "Guide"); private boolean disableIntegerFormat = false; + private boolean syncCorsPreflightIntegration = false; private ErrorStatusConflictHandlingStrategy onErrorStatusConflict; private OpenApiVersion version = OpenApiVersion.VERSION_3_0_2; @@ -355,6 +356,21 @@ public void setDisableIntegerFormat(boolean disableIntegerFormat) { } + public boolean getSyncCorsPreflightIntegration() { + return this.syncCorsPreflightIntegration; + } + + /** + * Set true to sync CORS preflight integration request templates with the other + * methods of the same path resource and set passthroughBehavior to "never". + * + * @param syncCorsPreflightIntegration True to match CORS preflight integration. + */ + public void setSyncCorsPreflightIntegration(boolean syncCorsPreflightIntegration) { + this.syncCorsPreflightIntegration = syncCorsPreflightIntegration; + } + + public ErrorStatusConflictHandlingStrategy getOnErrorStatusConflict() { return onErrorStatusConflict; }