This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch openapi2 in repository https://gitbox.apache.org/repos/asf/camel.git
commit b40dc7c1a8006501cb064aadd25fc6571ec6d055 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Mar 24 12:59:35 2024 +0100 CAMEL-20557: Rest DSL to use openapi spec directly --- .../openapi/RestOpenApiComponentConfigurer.java | 12 ++ .../openapi/RestOpenApiEndpointConfigurer.java | 24 ++++ .../openapi/RestOpenApiEndpointUriFactory.java | 8 +- .../camel/component/rest/openapi/rest-openapi.json | 52 ++++---- .../src/main/docs/rest-openapi-component.adoc | 2 +- .../rest/openapi/RestOpenApiComponent.java | 14 +++ .../rest/openapi/RestOpenApiEndpoint.java | 135 +++++++++++++++++++-- .../apache/camel/spi/RestApiConsumerFactory.java | 2 +- .../camel/spi/RestOpenApiConsumerFactory.java | 4 +- 9 files changed, 218 insertions(+), 35 deletions(-) diff --git a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentConfigurer.java b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentConfigurer.java index 8626436ff7f..ba22d771a42 100644 --- a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentConfigurer.java +++ b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiComponentConfigurer.java @@ -25,8 +25,12 @@ public class RestOpenApiComponentConfigurer extends PropertyConfigurerSupport im case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; case "basepath": case "basePath": target.setBasePath(property(camelContext, java.lang.String.class, value)); return true; + case "bridgeerrorhandler": + case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true; case "componentname": case "componentName": target.setComponentName(property(camelContext, java.lang.String.class, value)); return true; + case "consumercomponentname": + case "consumerComponentName": target.setConsumerComponentName(property(camelContext, java.lang.String.class, value)); return true; case "consumes": target.setConsumes(property(camelContext, java.lang.String.class, value)); return true; case "host": target.setHost(property(camelContext, java.lang.String.class, value)); return true; case "lazystartproducer": @@ -53,8 +57,12 @@ public class RestOpenApiComponentConfigurer extends PropertyConfigurerSupport im case "autowiredEnabled": return boolean.class; case "basepath": case "basePath": return java.lang.String.class; + case "bridgeerrorhandler": + case "bridgeErrorHandler": return boolean.class; case "componentname": case "componentName": return java.lang.String.class; + case "consumercomponentname": + case "consumerComponentName": return java.lang.String.class; case "consumes": return java.lang.String.class; case "host": return java.lang.String.class; case "lazystartproducer": @@ -82,8 +90,12 @@ public class RestOpenApiComponentConfigurer extends PropertyConfigurerSupport im case "autowiredEnabled": return target.isAutowiredEnabled(); case "basepath": case "basePath": return target.getBasePath(); + case "bridgeerrorhandler": + case "bridgeErrorHandler": return target.isBridgeErrorHandler(); case "componentname": case "componentName": return target.getComponentName(); + case "consumercomponentname": + case "consumerComponentName": return target.getConsumerComponentName(); case "consumes": return target.getConsumes(); case "host": return target.getHost(); case "lazystartproducer": diff --git a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointConfigurer.java b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointConfigurer.java index 664a2cb990a..3c8adaec8e9 100644 --- a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointConfigurer.java +++ b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointConfigurer.java @@ -23,9 +23,17 @@ public class RestOpenApiEndpointConfigurer extends PropertyConfigurerSupport imp switch (ignoreCase ? name.toLowerCase() : name) { case "basepath": case "basePath": target.setBasePath(property(camelContext, java.lang.String.class, value)); return true; + case "bridgeerrorhandler": + case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true; case "componentname": case "componentName": target.setComponentName(property(camelContext, java.lang.String.class, value)); return true; + case "consumercomponentname": + case "consumerComponentName": target.setConsumerComponentName(property(camelContext, java.lang.String.class, value)); return true; case "consumes": target.setConsumes(property(camelContext, java.lang.String.class, value)); return true; + case "exceptionhandler": + case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true; + case "exchangepattern": + case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true; case "host": target.setHost(property(camelContext, java.lang.String.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; @@ -45,9 +53,17 @@ public class RestOpenApiEndpointConfigurer extends PropertyConfigurerSupport imp switch (ignoreCase ? name.toLowerCase() : name) { case "basepath": case "basePath": return java.lang.String.class; + case "bridgeerrorhandler": + case "bridgeErrorHandler": return boolean.class; case "componentname": case "componentName": return java.lang.String.class; + case "consumercomponentname": + case "consumerComponentName": return java.lang.String.class; case "consumes": return java.lang.String.class; + case "exceptionhandler": + case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class; + case "exchangepattern": + case "exchangePattern": return org.apache.camel.ExchangePattern.class; case "host": return java.lang.String.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; @@ -68,9 +84,17 @@ public class RestOpenApiEndpointConfigurer extends PropertyConfigurerSupport imp switch (ignoreCase ? name.toLowerCase() : name) { case "basepath": case "basePath": return target.getBasePath(); + case "bridgeerrorhandler": + case "bridgeErrorHandler": return target.isBridgeErrorHandler(); case "componentname": case "componentName": return target.getComponentName(); + case "consumercomponentname": + case "consumerComponentName": return target.getConsumerComponentName(); case "consumes": return target.getConsumes(); + case "exceptionhandler": + case "exceptionHandler": return target.getExceptionHandler(); + case "exchangepattern": + case "exchangePattern": return target.getExchangePattern(); case "host": return target.getHost(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); diff --git a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriFactory.java b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriFactory.java index 43d1ec6ff59..8cf0fd1124f 100644 --- a/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriFactory.java +++ b/components/camel-rest-openapi/src/generated/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpointUriFactory.java @@ -21,10 +21,14 @@ public class RestOpenApiEndpointUriFactory extends org.apache.camel.support.comp private static final Set<String> SECRET_PROPERTY_NAMES; private static final Set<String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(11); + Set<String> props = new HashSet<>(15); props.add("basePath"); + props.add("bridgeErrorHandler"); props.add("componentName"); + props.add("consumerComponentName"); props.add("consumes"); + props.add("exceptionHandler"); + props.add("exchangePattern"); props.add("host"); props.add("lazyStartProducer"); props.add("operationId"); @@ -53,7 +57,7 @@ public class RestOpenApiEndpointUriFactory extends org.apache.camel.support.comp Map<String, Object> copy = new HashMap<>(properties); uri = buildPathParameter(syntax, uri, "specificationUri", "openapi.json", false, copy); - uri = buildPathParameter(syntax, uri, "operationId", null, true, copy); + uri = buildPathParameter(syntax, uri, "operationId", null, false, copy); uri = buildQueryParameters(uri, copy, encode); return uri; } diff --git a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json index 9242b3b9735..b817e5a6551 100644 --- a/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json +++ b/components/camel-rest-openapi/src/generated/resources/META-INF/org/apache/camel/component/rest/openapi/rest-openapi.json @@ -18,35 +18,41 @@ "async": false, "api": false, "consumerOnly": false, - "producerOnly": true, + "producerOnly": false, "lenientProperties": false, "remote": true }, "componentProperties": { - "basePath": { "index": 0, "kind": "property", "displayName": "Base Path", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "API basePath, for example \/v2. Default is unset, if set overrides the value present in OpenApi specification." }, - "host": { "index": 1, "kind": "property", "displayName": "Host", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Scheme hostname and port to direct the HTTP requests to in the form of https:\/\/hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a nam [...] - "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...] - "requestValidationEnabled": { "index": 3, "kind": "property", "displayName": "Request Validation Enabled", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable validation of requests against the configured OpenAPI specification" }, - "specificationUri": { "index": 4, "kind": "property", "displayName": "Specification Uri", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "openapi.json", "description": "Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If [...] - "componentName": { "index": 5, "kind": "property", "displayName": "Component Name", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH [...] - "consumes": { "index": 6, "kind": "property", "displayName": "Consumes", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component capable of consuming. Could be one type, like application\/json or multiple types as application\/json, application\/xml; q=0.5 according to the RFC7231. This equates to the val [...] - "produces": { "index": 7, "kind": "property", "displayName": "Produces", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component is producing. For example application\/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi sp [...] - "autowiredEnabled": { "index": 8, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] - "requestValidationCustomizer": { "index": 9, "kind": "property", "displayName": "Request Validation Customizer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.rest.openapi.validator.RequestValidationCustomizer", "deprecated": false, "autowired": false, "secret": false, "description": "If request validation is enabled, this option provides the capability to customize the creation of OpenApiInteractionValidator us [...] - "sslContextParameters": { "index": 10, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "description": "Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context" }, - "useGlobalSslContextParameters": { "index": 11, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } + "requestValidationEnabled": { "index": 0, "kind": "property", "displayName": "Request Validation Enabled", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable validation of requests against the configured OpenAPI specification" }, + "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the like [...] + "consumerComponentName": { "index": 2, "kind": "property", "displayName": "Consumer Component Name", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will service the requests. The component must be present in Camel registry and it must implement RestOpenApiConsumerFactory service provider interfac [...] + "basePath": { "index": 3, "kind": "property", "displayName": "Base Path", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "API basePath, for example \/v2. Default is unset, if set overrides the value present in OpenApi specification." }, + "host": { "index": 4, "kind": "property", "displayName": "Host", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Scheme hostname and port to direct the HTTP requests to in the form of https:\/\/hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a nam [...] + "lazyStartProducer": { "index": 5, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...] + "specificationUri": { "index": 6, "kind": "property", "displayName": "Specification Uri", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "openapi.json", "description": "Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If [...] + "componentName": { "index": 7, "kind": "property", "displayName": "Component Name", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH [...] + "consumes": { "index": 8, "kind": "property", "displayName": "Consumes", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component capable of consuming. Could be one type, like application\/json or multiple types as application\/json, application\/xml; q=0.5 according to the RFC7231. This equates to the val [...] + "produces": { "index": 9, "kind": "property", "displayName": "Produces", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component is producing. For example application\/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi sp [...] + "autowiredEnabled": { "index": 10, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...] + "requestValidationCustomizer": { "index": 11, "kind": "property", "displayName": "Request Validation Customizer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.rest.openapi.validator.RequestValidationCustomizer", "deprecated": false, "autowired": false, "secret": false, "description": "If request validation is enabled, this option provides the capability to customize the creation of OpenApiInteractionValidator u [...] + "sslContextParameters": { "index": 12, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "description": "Customize TLS parameters used by the component. If not set defaults to the TLS parameters set in the Camel context" }, + "useGlobalSslContextParameters": { "index": 13, "kind": "property", "displayName": "Use Global Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable usage of global SSL context parameters." } }, "properties": { "specificationUri": { "index": 0, "kind": "path", "displayName": "Specification Uri", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.net.URI", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "openapi.json", "description": "Path to the OpenApi specification file. The scheme, host base path are taken from this specification, but these can be overridden with properties on the component or endpoint level. If not [...] - "operationId": { "index": 1, "kind": "path", "displayName": "Operation Id", "group": "producer", "label": "producer", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "ID of the operation from the OpenApi specification." }, - "basePath": { "index": 2, "kind": "parameter", "displayName": "Base Path", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "API basePath, for example \/v3. Default is unset, if set overrides the value present in OpenApi specification and in the component configuration." }, - "consumes": { "index": 3, "kind": "parameter", "displayName": "Consumes", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component capable of consuming. Could be one type, like application\/json or multiple types as application\/json, application\/xml; q=0.5 according to the RFC7231. This equates to the value of Accept HTTP h [...] - "host": { "index": 4, "kind": "parameter", "displayName": "Host", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Scheme hostname and port to direct the HTTP requests to in the form of https:\/\/hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a na [...] - "produces": { "index": 5, "kind": "parameter", "displayName": "Produces", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component is producing. For example application\/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi specification. Overri [...] - "requestValidationEnabled": { "index": 6, "kind": "parameter", "displayName": "Request Validation Enabled", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable validation of requests against the configured OpenAPI specification" }, - "componentName": { "index": 7, "kind": "parameter", "displayName": "Component Name", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPATH [...] - "lazyStartProducer": { "index": 8, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...] - "requestValidationCustomizer": { "index": 9, "kind": "parameter", "displayName": "Request Validation Customizer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.rest.openapi.validator.RequestValidationCustomizer", "deprecated": false, "autowired": false, "secret": false, "description": "If request validation is enabled, this option provides the capability to customize the creation of OpenApiInteractionValidator u [...] - "requestValidationLevels": { "index": 10, "kind": "parameter", "displayName": "Request Validation Levels", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "validation.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "Levels for specific OpenAPI request validation options. Multiple options can be specified as URI options prefixed by ' [...] + "operationId": { "index": 1, "kind": "path", "displayName": "Operation Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "ID of the operation from the OpenApi specification. This is required when using producer" }, + "requestValidationEnabled": { "index": 2, "kind": "parameter", "displayName": "Request Validation Enabled", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enable validation of requests against the configured OpenAPI specification" }, + "bridgeErrorHandler": { "index": 3, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming [...] + "consumerComponentName": { "index": 4, "kind": "parameter", "displayName": "Consumer Component Name", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will service the requests. The component must be present in Camel registry and it must implement RestOpenApiConsumerFactory service provider interfa [...] + "exceptionHandler": { "index": 5, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By def [...] + "exchangePattern": { "index": 6, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, + "basePath": { "index": 7, "kind": "parameter", "displayName": "Base Path", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "API basePath, for example \/v3. Default is unset, if set overrides the value present in OpenApi specification and in the component configuration." }, + "consumes": { "index": 8, "kind": "parameter", "displayName": "Consumes", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component capable of consuming. Could be one type, like application\/json or multiple types as application\/json, application\/xml; q=0.5 according to the RFC7231. This equates to the value of Accept HTTP h [...] + "host": { "index": 9, "kind": "parameter", "displayName": "Host", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Scheme hostname and port to direct the HTTP requests to in the form of https:\/\/hostname:port. Can be configured at the endpoint, component or in the corresponding REST configuration in the Camel Context. If you give this component a na [...] + "produces": { "index": 10, "kind": "parameter", "displayName": "Produces", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "What payload type this component is producing. For example application\/json according to the RFC7231. This equates to the value of Content-Type HTTP header. If set overrides any value present in the OpenApi specification. Overr [...] + "componentName": { "index": 11, "kind": "parameter", "displayName": "Component Name", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of the Camel component that will perform the requests. The component must be present in Camel registry and it must implement RestProducerFactory service provider interface. If not set CLASSPAT [...] + "lazyStartProducer": { "index": 12, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...] + "requestValidationCustomizer": { "index": 13, "kind": "parameter", "displayName": "Request Validation Customizer", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.rest.openapi.validator.RequestValidationCustomizer", "deprecated": false, "autowired": false, "secret": false, "description": "If request validation is enabled, this option provides the capability to customize the creation of OpenApiInteractionValidator [...] + "requestValidationLevels": { "index": 14, "kind": "parameter", "displayName": "Request Validation Levels", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "prefix": "validation.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "Levels for specific OpenAPI request validation options. Multiple options can be specified as URI options prefixed by ' [...] } } diff --git a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc index c580baafcd4..e521f8767d0 100644 --- a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc +++ b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc @@ -6,7 +6,7 @@ :since: 3.1 :supportlevel: Stable :tabs-sync-option: -:component-header: Only producer is supported +:component-header: Both producer and consumer are supported //Manually maintained attributes :camel-spring-boot-name: rest-openapi diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java index ec4acc1707d..497431226a8 100644 --- a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java +++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java @@ -94,6 +94,12 @@ public final class RestOpenApiComponent extends DefaultComponent implements SSLC + " endpoint configuration.", label = "producer,advanced") private String componentName; + @Metadata(description = "Name of the Camel component that will service the requests. The component must be present" + + " in Camel registry and it must implement RestOpenApiConsumerFactory service provider interface. If not set" + + " CLASSPATH is searched for single component that implements RestOpenApiConsumerFactory SPI. Can be overridden in" + + " endpoint configuration.", + label = "consumer,advanced") + private String consumerComponentName; @Metadata(description = "Scheme hostname and port to direct the HTTP requests to in the form of" + " `http[s]://hostname[:port]`. Can be configured at the endpoint, component or in the corresponding" + " REST configuration in the Camel Context. If you give this component a name (e.g. `petstore`) that" @@ -158,6 +164,10 @@ public final class RestOpenApiComponent extends DefaultComponent implements SSLC return componentName; } + public String getConsumerComponentName() { + return consumerComponentName; + } + public String getConsumes() { return consumes; } @@ -191,6 +201,10 @@ public final class RestOpenApiComponent extends DefaultComponent implements SSLC this.componentName = notEmpty(componentName, "componentName"); } + public void setConsumerComponentName(String consumerComponentName) { + this.consumerComponentName = consumerComponentName; + } + public void setConsumes(final String consumes) { this.consumes = isMediaRange(consumes, "consumes"); } diff --git a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java index bb80f463521..1fd6f314adc 100644 --- a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java +++ b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java @@ -54,18 +54,20 @@ import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; import org.apache.camel.CamelContext; import org.apache.camel.Category; +import org.apache.camel.Component; import org.apache.camel.Consumer; import org.apache.camel.Endpoint; import org.apache.camel.ExchangePattern; +import org.apache.camel.NoSuchBeanException; import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.component.rest.openapi.validator.DefaultRequestValidationCustomizer; import org.apache.camel.component.rest.openapi.validator.RequestValidationCustomizer; import org.apache.camel.component.rest.openapi.validator.RequestValidator; import org.apache.camel.component.rest.openapi.validator.RestOpenApiOperation; -import org.apache.camel.spi.Metadata; import org.apache.camel.spi.Resource; import org.apache.camel.spi.RestConfiguration; +import org.apache.camel.spi.RestOpenApiConsumerFactory; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.spi.UriPath; @@ -77,6 +79,7 @@ import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.StringHelper; import org.apache.camel.util.UnsafeUriCharactersEncoder; import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.Optional.ofNullable; @@ -92,10 +95,14 @@ import static org.apache.camel.util.StringHelper.notEmpty; * To call REST services using OpenAPI specification as contract. */ @UriEndpoint(firstVersion = "3.1.0", scheme = "rest-openapi", title = "REST OpenApi", - syntax = "rest-openapi:specificationUri#operationId", category = { Category.REST, Category.API }, - producerOnly = true) + syntax = "rest-openapi:specificationUri#operationId", category = { Category.REST, Category.API }) public final class RestOpenApiEndpoint extends DefaultEndpoint { + private static final Logger LOG = LoggerFactory.getLogger(RestOpenApiEndpoint.class); + + public static final String[] DEFAULT_REST_OPENAPI_CONSUMER_COMPONENTS + = new String[] { "platform-http" }; + /** * Remaining parameters specified in the Endpoint URI. */ @@ -111,6 +118,12 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { + " component configuration.", label = "producer,advanced") private String componentName; + @UriParam(description = "Name of the Camel component that will service the requests. The component must be present" + + " in Camel registry and it must implement RestOpenApiConsumerFactory service provider interface. If not set" + + " CLASSPATH is searched for single component that implements RestOpenApiConsumerFactory SPI. Overrides" + + " component configuration.", + label = "consumer,advanced") + private String consumerComponentName; @UriParam(description = "Scheme hostname and port to direct the HTTP requests to in the form of" + " `http[s]://hostname[:port]`. Can be configured at the endpoint, component or in the corresponding" + " REST configuration in the Camel Context. If you give this component a name (e.g. `petstore`) that" @@ -119,8 +132,8 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { + " configuration.", label = "producer") private String host; - @UriPath(description = "ID of the operation from the OpenApi specification.", label = "producer") - @Metadata(required = true) + @UriPath(description = "ID of the operation from the OpenApi specification. This is required when using producer", + label = "producer") private String operationId; @UriParam(description = "What payload type this component capable of consuming. Could be one type, like `application/json`" + " or multiple types as `application/json, application/xml; q=0.5` according to the RFC7231. This equates" @@ -182,7 +195,103 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { @Override public Consumer createConsumer(final Processor processor) throws Exception { - throw new UnsupportedOperationException("Consumer not supported"); + final CamelContext camelContext = getCamelContext(); + final OpenAPI openapiDoc = loadSpecificationFrom(camelContext, specificationUri); + String path = determineBasePath(openapiDoc); + + // TODO: processor should use OpenAPI to detect which operations exists, and map to direct:xx + // TODO: validate on|poff + + return createConsumerFor(path, processor); + } + + protected Consumer createConsumerFor(String basePath, Processor processor) throws Exception { + RestOpenApiConsumerFactory factory = null; + String cname = null; + if (getConsumerComponentName() != null) { + Object comp = getCamelContext().getRegistry().lookupByName(getConsumerComponentName()); + if (comp instanceof RestOpenApiConsumerFactory) { + factory = (RestOpenApiConsumerFactory) comp; + } else { + comp = getCamelContext().getComponent(getConsumerComponentName()); + if (comp instanceof RestOpenApiConsumerFactory) { + factory = (RestOpenApiConsumerFactory) comp; + } + } + + if (factory == null) { + if (comp != null) { + throw new IllegalArgumentException( + "Component " + getConsumerComponentName() + " is not a RestOpenApiConsumerFactory"); + } else { + throw new NoSuchBeanException(getConsumerComponentName(), RestOpenApiConsumerFactory.class.getName()); + } + } + cname = getConsumerComponentName(); + } + + // try all components + if (factory == null) { + for (String name : getCamelContext().getComponentNames()) { + Component comp = getCamelContext().getComponent(name); + if (comp instanceof RestOpenApiConsumerFactory) { + factory = (RestOpenApiConsumerFactory) comp; + cname = name; + break; + } + } + } + + // favour using platform-http if available on classpath + if (factory == null) { + Object comp = getCamelContext().getComponent("platform-http", true); + if (comp instanceof RestOpenApiConsumerFactory) { + factory = (RestOpenApiConsumerFactory) comp; + LOG.debug("Auto discovered platform-http as RestConsumerFactory"); + } + } + + // lookup in registry + if (factory == null) { + Set<RestOpenApiConsumerFactory> factories + = getCamelContext().getRegistry().findByType(RestOpenApiConsumerFactory.class); + if (factories != null && factories.size() == 1) { + factory = factories.iterator().next(); + } + } + + // no explicit factory found then try to see if we can find any of the default rest consumer components + // and there must only be exactly one so we safely can pick this one + if (factory == null) { + RestOpenApiConsumerFactory found = null; + String foundName = null; + for (String name : DEFAULT_REST_OPENAPI_CONSUMER_COMPONENTS) { + Object comp = getCamelContext().getComponent(name, true); + if (comp instanceof RestOpenApiConsumerFactory) { + if (found == null) { + found = (RestOpenApiConsumerFactory) comp; + foundName = name; + } else { + throw new IllegalArgumentException( + "Multiple RestOpenApiConsumerFactory found on classpath. Configure explicit which component to use"); + } + } + } + if (found != null) { + LOG.debug("Auto discovered {} as RestOpenApiConsumerFactory", foundName); + factory = found; + } + } + + if (factory != null) { + RestConfiguration config = CamelContextHelper.getRestConfiguration(getCamelContext(), cname); + Map<String, Object> copy = new HashMap<>(parameters); // defensive copy of the parameters + Consumer consumer = factory.createConsumer(getCamelContext(), processor, basePath, config, copy); + configureConsumer(consumer); + return consumer; + } else { + throw new IllegalStateException("Cannot find RestOpenApiConsumerFactory in Registry or as a Component to use"); + } } @Override @@ -237,6 +346,10 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { return componentName; } + public String getConsumerComponentName() { + return consumerComponentName; + } + public String getConsumes() { return consumes; } @@ -267,7 +380,11 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { } public void setComponentName(final String componentName) { - this.componentName = notEmpty(componentName, "componentName"); + this.componentName = componentName; + } + + public void setConsumerComponentName(String consumerComponentName) { + this.consumerComponentName = consumerComponentName; } public void setConsumes(final String consumes) { @@ -418,6 +535,10 @@ public final class RestOpenApiEndpoint extends DefaultEndpoint { return Optional.ofNullable(componentName).orElse(getComponent().getComponentName()); } + String determineConsumerComponentName() { + return Optional.ofNullable(consumerComponentName).orElse(getComponent().getConsumerComponentName()); + } + Map<String, Object> determineEndpointParameters(final OpenAPI openapi, final Operation operation) { final Map<String, Object> parameters = new HashMap<>(); diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RestApiConsumerFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/RestApiConsumerFactory.java index e2a6dcc5b96..82e5b9e735e 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/RestApiConsumerFactory.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/RestApiConsumerFactory.java @@ -38,8 +38,8 @@ public interface RestApiConsumerFactory { * @param camelContext the camel context * @param processor the processor * @param contextPath the context-path + * @param configuration REST configuration * @param parameters additional parameters - * * @return a newly created REST API consumer * @throws Exception can be thrown */ diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java b/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java index 5022a1b8bf2..99a2ff513fa 100644 --- a/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java +++ b/core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiConsumerFactory.java @@ -39,12 +39,14 @@ public interface RestOpenApiConsumerFactory { * * @param camelContext the camel context * @param processor the processor + * @param contextPath the context-path * @param configuration REST configuration * @param parameters additional parameters * @return a newly created REST consumer * @throws Exception can be thrown */ Consumer createConsumer( - CamelContext camelContext, Processor processor, RestConfiguration configuration, Map<String, Object> parameters) + CamelContext camelContext, Processor processor, String contextPath, + RestConfiguration configuration, Map<String, Object> parameters) throws Exception; }