This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch CAMEL-20552 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 484f795763f680fbda8b73505a71d9e1dae1e0ca Author: Andrea Cosentino <[email protected]> AuthorDate: Mon Nov 17 18:44:31 2025 +0100 CAMEL-20552 - Create an Azure Event Grid component Signed-off-by: Andrea Cosentino <[email protected]> --- bom/camel-bom/pom.xml | 5 + catalog/camel-allcomponents/pom.xml | 5 + .../camel-azure/camel-azure-eventgrid/pom.xml | 72 ++++++++++ .../eventgrid/EventGridComponentConfigurer.java | 111 ++++++++++++++ .../eventgrid/EventGridEndpointConfigurer.java | 95 ++++++++++++ .../eventgrid/EventGridEndpointUriFactory.java | 80 +++++++++++ .../component/azure/eventgrid/azure-eventgrid.json | 52 +++++++ .../services/org/apache/camel/component.properties | 7 + .../org/apache/camel/component/azure-eventgrid | 2 + .../camel/configurer/azure-eventgrid-component | 2 + .../camel/configurer/azure-eventgrid-endpoint | 2 + .../camel/urifactory/azure-eventgrid-endpoint | 2 + .../component/azure/eventgrid/CredentialType.java | 39 +++++ .../azure/eventgrid/EventGridComponent.java | 115 +++++++++++++++ .../azure/eventgrid/EventGridConfiguration.java | 126 ++++++++++++++++ .../azure/eventgrid/EventGridConstants.java | 41 ++++++ .../azure/eventgrid/EventGridEndpoint.java | 90 ++++++++++++ .../azure/eventgrid/EventGridProducer.java | 160 +++++++++++++++++++++ .../org/apache/camel/component/azure-eventgrid | 1 + .../azure/eventgrid/EventGridComponentTest.java | 100 +++++++++++++ components/camel-azure/pom.xml | 1 + .../ROOT/examples/json/azure-eventgrid.json | 1 + parent/pom.xml | 5 + 23 files changed, 1114 insertions(+) diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index 35074ea948e7..b78616a5ab9c 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -276,6 +276,11 @@ <artifactId>camel-azure-cosmosdb</artifactId> <version>4.17.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-azure-eventgrid</artifactId> + <version>4.17.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-azure-eventhubs</artifactId> diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 7edb1f43e46b..6b79e23afacf 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -276,6 +276,11 @@ <artifactId>camel-azure-cosmosdb</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-azure-eventgrid</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-azure-eventhubs</artifactId> diff --git a/components/camel-azure/camel-azure-eventgrid/pom.xml b/components/camel-azure/camel-azure-eventgrid/pom.xml new file mode 100644 index 000000000000..32334343bd63 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/pom.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>camel-azure-parent</artifactId> + <version>4.17.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-azure-eventgrid</artifactId> + <packaging>jar</packaging> + + <name>Camel :: Azure :: Event Grid</name> + <description>Camel Azure Event Grid Component</description> + + <properties> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-support</artifactId> + </dependency> + + <!-- azure sdk --> + <dependency> + <groupId>com.azure</groupId> + <artifactId>azure-messaging-eventgrid</artifactId> + </dependency> + <dependency> + <groupId>com.azure</groupId> + <artifactId>azure-identity</artifactId> + </dependency> + + <!-- for testing --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-test-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridComponentConfigurer.java b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridComponentConfigurer.java new file mode 100644 index 000000000000..a3b9b81df4b6 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridComponentConfigurer.java @@ -0,0 +1,111 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.azure.eventgrid; + +import javax.annotation.processing.Generated; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo") +@SuppressWarnings("unchecked") +public class EventGridComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + private org.apache.camel.component.azure.eventgrid.EventGridConfiguration getOrCreateConfiguration(EventGridComponent target) { + if (target.getConfiguration() == null) { + target.setConfiguration(new org.apache.camel.component.azure.eventgrid.EventGridConfiguration()); + } + return target.getConfiguration(); + } + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + EventGridComponent target = (EventGridComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": getOrCreateConfiguration(target).setAccessKey(property(camelContext, java.lang.String.class, value)); return true; + case "autowiredenabled": + case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; + case "azurekeycredential": + case "azureKeyCredential": getOrCreateConfiguration(target).setAzureKeyCredential(property(camelContext, com.azure.core.credential.AzureKeyCredential.class, value)); return true; + case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.azure.eventgrid.EventGridConfiguration.class, value)); return true; + case "credentialtype": + case "credentialType": getOrCreateConfiguration(target).setCredentialType(property(camelContext, org.apache.camel.component.azure.eventgrid.CredentialType.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "publisherclient": + case "publisherClient": getOrCreateConfiguration(target).setPublisherClient(property(camelContext, com.azure.messaging.eventgrid.EventGridPublisherClient.class, value)); return true; + case "tokencredential": + case "tokenCredential": getOrCreateConfiguration(target).setTokenCredential(property(camelContext, com.azure.core.credential.TokenCredential.class, value)); return true; + default: return false; + } + } + + @Override + public String[] getAutowiredNames() { + return new String[]{"azureKeyCredential", "publisherClient", "tokenCredential"}; + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": return java.lang.String.class; + case "autowiredenabled": + case "autowiredEnabled": return boolean.class; + case "azurekeycredential": + case "azureKeyCredential": return com.azure.core.credential.AzureKeyCredential.class; + case "configuration": return org.apache.camel.component.azure.eventgrid.EventGridConfiguration.class; + case "credentialtype": + case "credentialType": return org.apache.camel.component.azure.eventgrid.CredentialType.class; + case "lazystartproducer": + case "lazyStartProducer": return boolean.class; + case "publisherclient": + case "publisherClient": return com.azure.messaging.eventgrid.EventGridPublisherClient.class; + case "tokencredential": + case "tokenCredential": return com.azure.core.credential.TokenCredential.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + EventGridComponent target = (EventGridComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": return getOrCreateConfiguration(target).getAccessKey(); + case "autowiredenabled": + case "autowiredEnabled": return target.isAutowiredEnabled(); + case "azurekeycredential": + case "azureKeyCredential": return getOrCreateConfiguration(target).getAzureKeyCredential(); + case "configuration": return target.getConfiguration(); + case "credentialtype": + case "credentialType": return getOrCreateConfiguration(target).getCredentialType(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + case "publisherclient": + case "publisherClient": return getOrCreateConfiguration(target).getPublisherClient(); + case "tokencredential": + case "tokenCredential": return getOrCreateConfiguration(target).getTokenCredential(); + default: return null; + } + } + + @Override + public Object getCollectionValueType(Object target, String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "publisherclient": + case "publisherClient": return com.azure.core.models.CloudEvent.class; + default: return null; + } + } +} + diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointConfigurer.java b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointConfigurer.java new file mode 100644 index 000000000000..70d7eedd5207 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointConfigurer.java @@ -0,0 +1,95 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.azure.eventgrid; + +import javax.annotation.processing.Generated; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo") +@SuppressWarnings("unchecked") +public class EventGridEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + EventGridEndpoint target = (EventGridEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": target.getConfiguration().setAccessKey(property(camelContext, java.lang.String.class, value)); return true; + case "azurekeycredential": + case "azureKeyCredential": target.getConfiguration().setAzureKeyCredential(property(camelContext, com.azure.core.credential.AzureKeyCredential.class, value)); return true; + case "credentialtype": + case "credentialType": target.getConfiguration().setCredentialType(property(camelContext, org.apache.camel.component.azure.eventgrid.CredentialType.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "publisherclient": + case "publisherClient": target.getConfiguration().setPublisherClient(property(camelContext, com.azure.messaging.eventgrid.EventGridPublisherClient.class, value)); return true; + case "tokencredential": + case "tokenCredential": target.getConfiguration().setTokenCredential(property(camelContext, com.azure.core.credential.TokenCredential.class, value)); return true; + default: return false; + } + } + + @Override + public String[] getAutowiredNames() { + return new String[]{"azureKeyCredential", "publisherClient", "tokenCredential"}; + } + + @Override + public Class<?> getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": return java.lang.String.class; + case "azurekeycredential": + case "azureKeyCredential": return com.azure.core.credential.AzureKeyCredential.class; + case "credentialtype": + case "credentialType": return org.apache.camel.component.azure.eventgrid.CredentialType.class; + case "lazystartproducer": + case "lazyStartProducer": return boolean.class; + case "publisherclient": + case "publisherClient": return com.azure.messaging.eventgrid.EventGridPublisherClient.class; + case "tokencredential": + case "tokenCredential": return com.azure.core.credential.TokenCredential.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + EventGridEndpoint target = (EventGridEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "accesskey": + case "accessKey": return target.getConfiguration().getAccessKey(); + case "azurekeycredential": + case "azureKeyCredential": return target.getConfiguration().getAzureKeyCredential(); + case "credentialtype": + case "credentialType": return target.getConfiguration().getCredentialType(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + case "publisherclient": + case "publisherClient": return target.getConfiguration().getPublisherClient(); + case "tokencredential": + case "tokenCredential": return target.getConfiguration().getTokenCredential(); + default: return null; + } + } + + @Override + public Object getCollectionValueType(Object target, String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "publisherclient": + case "publisherClient": return com.azure.core.models.CloudEvent.class; + default: return null; + } + } +} + diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointUriFactory.java b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointUriFactory.java new file mode 100644 index 000000000000..35f146868c54 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/java/org/apache/camel/component/azure/eventgrid/EventGridEndpointUriFactory.java @@ -0,0 +1,80 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.azure.eventgrid; + +import javax.annotation.processing.Generated; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.camel.spi.EndpointUriFactory; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo") +public class EventGridEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory { + + private static final String BASE = ":topicEndpoint"; + + private static final Set<String> PROPERTY_NAMES; + private static final Set<String> SECRET_PROPERTY_NAMES; + private static final Map<String, String> MULTI_VALUE_PREFIXES; + static { + Set<String> props = new HashSet<>(7); + props.add("accessKey"); + props.add("azureKeyCredential"); + props.add("credentialType"); + props.add("lazyStartProducer"); + props.add("publisherClient"); + props.add("tokenCredential"); + props.add("topicEndpoint"); + PROPERTY_NAMES = Collections.unmodifiableSet(props); + Set<String> secretProps = new HashSet<>(3); + secretProps.add("accessKey"); + secretProps.add("azureKeyCredential"); + secretProps.add("tokenCredential"); + SECRET_PROPERTY_NAMES = Collections.unmodifiableSet(secretProps); + MULTI_VALUE_PREFIXES = Collections.emptyMap(); + } + + @Override + public boolean isEnabled(String scheme) { + return "azure-eventgrid".equals(scheme); + } + + @Override + public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException { + String syntax = scheme + BASE; + String uri = syntax; + + Map<String, Object> copy = new HashMap<>(properties); + + uri = buildPathParameter(syntax, uri, "topicEndpoint", null, true, copy); + uri = buildQueryParameters(uri, copy, encode); + return uri; + } + + @Override + public Set<String> propertyNames() { + return PROPERTY_NAMES; + } + + @Override + public Set<String> secretPropertyNames() { + return SECRET_PROPERTY_NAMES; + } + + @Override + public Map<String, String> multiValuePrefixes() { + return MULTI_VALUE_PREFIXES; + } + + @Override + public boolean isLenientProperties() { + return false; + } +} + diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/org/apache/camel/component/azure/eventgrid/azure-eventgrid.json b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/org/apache/camel/component/azure/eventgrid/azure-eventgrid.json new file mode 100644 index 000000000000..8a16730595f9 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/org/apache/camel/component/azure/eventgrid/azure-eventgrid.json @@ -0,0 +1,52 @@ +{ + "component": { + "kind": "component", + "name": "azure-eventgrid", + "title": "Azure Event Grid", + "description": "Send events to Azure Event Grid topics.", + "deprecated": false, + "firstVersion": "4.17.0", + "label": "cloud,messaging", + "javaType": "org.apache.camel.component.azure.eventgrid.EventGridComponent", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-azure-eventgrid", + "version": "4.17.0-SNAPSHOT", + "scheme": "azure-eventgrid", + "extendsScheme": "", + "syntax": "azure-eventgrid:topicEndpoint", + "async": false, + "api": false, + "consumerOnly": false, + "producerOnly": true, + "lenientProperties": false, + "browsable": false, + "remote": true + }, + "componentProperties": { + "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The component configurations" }, + "lazyStartProducer": { "index": 1, "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 [...] + "publisherClient": { "index": 2, "kind": "property", "displayName": "Publisher Client", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "com.azure.messaging.eventgrid.EventGridPublisherClient<com.azure.core.models.CloudEvent>", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "descri [...] + "autowiredEnabled": { "index": 3, "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 [...] + "accessKey": { "index": 4, "kind": "property", "displayName": "Access Key", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "The access key for the Event Grid topic. Required when using ACCESS_KEY credential type." }, + "azureKeyCredential": { "index": 5, "kind": "property", "displayName": "Azure Key Credential", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "com.azure.core.credential.AzureKeyCredential", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "The Azure Key Credential for [...] + "credentialType": { "index": 6, "kind": "property", "displayName": "Credential Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.azure.eventgrid.CredentialType", "enum": [ "ACCESS_KEY", "AZURE_IDENTITY", "TOKEN_CREDENTIAL" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "ACCESS_KEY", "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configur [...] + "tokenCredential": { "index": 7, "kind": "property", "displayName": "Token Credential", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "com.azure.core.credential.TokenCredential", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "Provide custom authentication credentia [...] + }, + "headers": { + "CamelAzureEventGridEventType": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The event type of the event.", "constantName": "org.apache.camel.component.azure.eventgrid.EventGridConstants#EVENT_TYPE" }, + "CamelAzureEventGridSubject": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The subject of the event.", "constantName": "org.apache.camel.component.azure.eventgrid.EventGridConstants#SUBJECT" }, + "CamelAzureEventGridEventTime": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "OffsetDateTime", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The time the event was generated.", "constantName": "org.apache.camel.component.azure.eventgrid.EventGridConstants#EVENT_TIME" }, + "CamelAzureEventGridId": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The unique identifier for the event.", "constantName": "org.apache.camel.component.azure.eventgrid.EventGridConstants#ID" }, + "CamelAzureEventGridDataVersion": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The schema version of the data object.", "constantName": "org.apache.camel.component.azure.eventgrid.EventGridConstants#DATA_VERSION" } + }, + "properties": { + "topicEndpoint": { "index": 0, "kind": "path", "displayName": "Topic Endpoint", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "The topic endpoint URL where events will be published." }, + "publisherClient": { "index": 1, "kind": "parameter", "displayName": "Publisher Client", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "com.azure.messaging.eventgrid.EventGridPublisherClient<com.azure.core.models.CloudEvent>", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "descr [...] + "lazyStartProducer": { "index": 2, "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 [...] + "accessKey": { "index": 3, "kind": "parameter", "displayName": "Access Key", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "The access key for the Event Grid topic. Required when using ACCESS_KEY credential type." }, + "azureKeyCredential": { "index": 4, "kind": "parameter", "displayName": "Azure Key Credential", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "com.azure.core.credential.AzureKeyCredential", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "The Azure Key Credential for [...] + "credentialType": { "index": 5, "kind": "parameter", "displayName": "Credential Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.azure.eventgrid.CredentialType", "enum": [ "ACCESS_KEY", "AZURE_IDENTITY", "TOKEN_CREDENTIAL" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "ACCESS_KEY", "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configu [...] + "tokenCredential": { "index": 6, "kind": "parameter", "displayName": "Token Credential", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "com.azure.core.credential.TokenCredential", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": true, "configurationClass": "org.apache.camel.component.azure.eventgrid.EventGridConfiguration", "configurationField": "configuration", "description": "Provide custom authentication credenti [...] + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component.properties new file mode 100644 index 000000000000..99f75e8c17f1 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component.properties @@ -0,0 +1,7 @@ +# Generated by camel build tools - do NOT edit this file! +components=azure-eventgrid +groupId=org.apache.camel +artifactId=camel-azure-eventgrid +version=4.17.0-SNAPSHOT +projectName=Camel :: Azure :: Event Grid +projectDescription=Camel Azure Event Grid Component diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component/azure-eventgrid b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component/azure-eventgrid new file mode 100644 index 000000000000..2530bcc72614 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/component/azure-eventgrid @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.azure.eventgrid.EventGridComponent diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-component b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-component new file mode 100644 index 000000000000..1b30694205e2 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-component @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.azure.eventgrid.EventGridComponentConfigurer diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-endpoint b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-endpoint new file mode 100644 index 000000000000..701da4995bfe --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/configurer/azure-eventgrid-endpoint @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.azure.eventgrid.EventGridEndpointConfigurer diff --git a/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-eventgrid-endpoint b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-eventgrid-endpoint new file mode 100644 index 000000000000..a6fa9bfa06fc --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/services/org/apache/camel/urifactory/azure-eventgrid-endpoint @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.azure.eventgrid.EventGridEndpointUriFactory diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/CredentialType.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/CredentialType.java new file mode 100644 index 000000000000..a091748c393e --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/CredentialType.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +public enum CredentialType { + /** + * EventGrid Access Key + */ + ACCESS_KEY, + /** + * Includes: + * <uL> + * <li>Service principal with secret</li> + * <li>Service principal with certificate</li> + * <li>username and password</li> + * </uL> + * + * @see com.azure.identity.DefaultAzureCredentialBuilder + */ + AZURE_IDENTITY, + /** + * EventGrid Token Credential + */ + TOKEN_CREDENTIAL, +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridComponent.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridComponent.java new file mode 100644 index 000000000000..4c9ffee79df1 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridComponent.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import java.util.Map; + +import com.azure.identity.DefaultAzureCredential; +import org.apache.camel.Endpoint; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.annotations.Component; +import org.apache.camel.support.DefaultComponent; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Azure EventGrid component + */ +@Component("azure-eventgrid") +public class EventGridComponent extends DefaultComponent { + + private static final Logger LOG = LoggerFactory.getLogger(EventGridComponent.class); + + @Metadata + private EventGridConfiguration configuration = new EventGridConfiguration(); + + public EventGridComponent() { + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { + final EventGridConfiguration configuration = this.configuration.copy(); + + // Set the topic endpoint from the remaining part if not using a custom client + if (configuration.getPublisherClient() == null && ObjectHelper.isNotEmpty(remaining)) { + configuration.setTopicEndpoint(remaining); + } + + final EventGridEndpoint endpoint = new EventGridEndpoint(uri, this, configuration); + setProperties(endpoint, parameters); + + // Ensure we use default credential type if not configured + if (endpoint.getConfiguration().getTokenCredential() == null + && endpoint.getConfiguration().getAzureKeyCredential() == null + && endpoint.getConfiguration().getAccessKey() == null) { + if (endpoint.getConfiguration().getCredentialType() == null) { + endpoint.getConfiguration().setCredentialType(CredentialType.AZURE_IDENTITY); + } + } else if (endpoint.getConfiguration().getTokenCredential() != null) { + boolean azure = endpoint.getConfiguration().getTokenCredential() instanceof DefaultAzureCredential; + endpoint.getConfiguration() + .setCredentialType(azure ? CredentialType.AZURE_IDENTITY : CredentialType.TOKEN_CREDENTIAL); + } else if (endpoint.getConfiguration().getAzureKeyCredential() != null + || endpoint.getConfiguration().getAccessKey() != null) { + endpoint.getConfiguration().setCredentialType(CredentialType.ACCESS_KEY); + } + + validateConfigurations(configuration); + + return endpoint; + } + + /** + * The component configurations + */ + public EventGridConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(EventGridConfiguration configuration) { + this.configuration = configuration; + } + + private void validateConfigurations(final EventGridConfiguration configuration) { + if (configuration.getPublisherClient() == null) { + if (ObjectHelper.isEmpty(configuration.getTopicEndpoint())) { + throw new IllegalArgumentException("Topic endpoint must be specified."); + } + + if (!isAccessKeySet(configuration) && !isTokenCredentialSet(configuration) + && !isAzureIdentitySet(configuration)) { + throw new IllegalArgumentException( + "Azure EventGrid AccessKey, AzureKeyCredential, TokenCredential or Azure Identity must be specified."); + } + } + } + + private boolean isAccessKeySet(final EventGridConfiguration configuration) { + return ObjectHelper.isNotEmpty(configuration.getAccessKey()) + || ObjectHelper.isNotEmpty(configuration.getAzureKeyCredential()); + } + + private boolean isTokenCredentialSet(final EventGridConfiguration configuration) { + return ObjectHelper.isNotEmpty(configuration.getTokenCredential()); + } + + private boolean isAzureIdentitySet(final EventGridConfiguration configuration) { + return ObjectHelper.isNotEmpty(configuration.getCredentialType()) + && configuration.getCredentialType().equals(CredentialType.AZURE_IDENTITY); + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConfiguration.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConfiguration.java new file mode 100644 index 000000000000..545f0ffdfc64 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConfiguration.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.credential.TokenCredential; +import com.azure.messaging.eventgrid.EventGridPublisherClient; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.UriParam; +import org.apache.camel.spi.UriParams; +import org.apache.camel.spi.UriPath; + +@UriParams +public class EventGridConfiguration implements Cloneable { + + @UriPath + @Metadata(required = true) + private String topicEndpoint; + + @UriParam(label = "security", secret = true) + private String accessKey; + + @UriParam(label = "security", secret = true) + @Metadata(autowired = true) + private AzureKeyCredential azureKeyCredential; + + @UriParam(label = "security", secret = true) + @Metadata(autowired = true) + private TokenCredential tokenCredential; + + @UriParam(label = "security", enums = "ACCESS_KEY,AZURE_IDENTITY,TOKEN_CREDENTIAL", defaultValue = "ACCESS_KEY") + private CredentialType credentialType = CredentialType.ACCESS_KEY; + + @UriParam(label = "producer") + @Metadata(autowired = true) + private EventGridPublisherClient<com.azure.core.models.CloudEvent> publisherClient; + + /** + * The topic endpoint URL where events will be published. + */ + public String getTopicEndpoint() { + return topicEndpoint; + } + + public void setTopicEndpoint(String topicEndpoint) { + this.topicEndpoint = topicEndpoint; + } + + /** + * The access key for the Event Grid topic. Required when using ACCESS_KEY credential type. + */ + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + /** + * The Azure Key Credential for authentication. This is automatically created from the accessKey if not provided. + */ + public AzureKeyCredential getAzureKeyCredential() { + return azureKeyCredential; + } + + public void setAzureKeyCredential(AzureKeyCredential azureKeyCredential) { + this.azureKeyCredential = azureKeyCredential; + } + + /** + * Provide custom authentication credentials using an implementation of {@link TokenCredential}. + */ + public TokenCredential getTokenCredential() { + return tokenCredential; + } + + public void setTokenCredential(TokenCredential tokenCredential) { + this.tokenCredential = tokenCredential; + } + + /** + * Determines the credential strategy to adopt + */ + public CredentialType getCredentialType() { + return credentialType; + } + + public void setCredentialType(CredentialType credentialType) { + this.credentialType = credentialType; + } + + /** + * The EventGrid publisher client. If provided, it will be used instead of creating a new one. + */ + public EventGridPublisherClient<com.azure.core.models.CloudEvent> getPublisherClient() { + return publisherClient; + } + + public void setPublisherClient(EventGridPublisherClient<com.azure.core.models.CloudEvent> publisherClient) { + this.publisherClient = publisherClient; + } + + public EventGridConfiguration copy() { + try { + return (EventGridConfiguration) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeCamelException(e); + } + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConstants.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConstants.java new file mode 100644 index 000000000000..ea35056e20cb --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridConstants.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import org.apache.camel.spi.Metadata; + +public final class EventGridConstants { + public static final String HEADER_PREFIX = "CamelAzureEventGrid"; + + @Metadata(description = "The event type of the event.", javaType = "String") + public static final String EVENT_TYPE = HEADER_PREFIX + "EventType"; + + @Metadata(description = "The subject of the event.", javaType = "String") + public static final String SUBJECT = HEADER_PREFIX + "Subject"; + + @Metadata(description = "The time the event was generated.", javaType = "OffsetDateTime") + public static final String EVENT_TIME = HEADER_PREFIX + "EventTime"; + + @Metadata(description = "The unique identifier for the event.", javaType = "String") + public static final String ID = HEADER_PREFIX + "Id"; + + @Metadata(description = "The schema version of the data object.", javaType = "String") + public static final String DATA_VERSION = HEADER_PREFIX + "DataVersion"; + + private EventGridConstants() { + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridEndpoint.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridEndpoint.java new file mode 100644 index 000000000000..0600086f0e10 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridEndpoint.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import java.util.Map; + +import org.apache.camel.Category; +import org.apache.camel.Component; +import org.apache.camel.Consumer; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.spi.EndpointServiceLocation; +import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.spi.UriParam; +import org.apache.camel.support.DefaultEndpoint; +import org.apache.camel.util.ObjectHelper; + +/** + * Send events to Azure Event Grid topics. + */ +@UriEndpoint(firstVersion = "4.17.0", scheme = "azure-eventgrid", title = "Azure Event Grid", + syntax = "azure-eventgrid:topicEndpoint", producerOnly = true, category = { + Category.CLOUD, Category.MESSAGING }, + headersClass = EventGridConstants.class) +public class EventGridEndpoint extends DefaultEndpoint implements EndpointServiceLocation { + + @UriParam + private EventGridConfiguration configuration; + + public EventGridEndpoint(final String uri, final Component component, final EventGridConfiguration configuration) { + super(uri, component); + this.configuration = configuration; + } + + @Override + public Producer createProducer() throws Exception { + return new EventGridProducer(this); + } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + throw new UnsupportedOperationException("Consumer is not supported for Azure Event Grid component"); + } + + /** + * The component configurations + */ + public EventGridConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(EventGridConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public String getServiceUrl() { + if (ObjectHelper.isNotEmpty(configuration.getTopicEndpoint())) { + return configuration.getTopicEndpoint(); + } + return null; + } + + @Override + public String getServiceProtocol() { + return "eventgrid"; + } + + @Override + public Map<String, String> getServiceMetadata() { + if (configuration.getTopicEndpoint() != null) { + return Map.of("topicEndpoint", configuration.getTopicEndpoint()); + } + return null; + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridProducer.java b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridProducer.java new file mode 100644 index 000000000000..5b0de8105eb5 --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/java/org/apache/camel/component/azure/eventgrid/EventGridProducer.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.azure.core.credential.AzureKeyCredential; +import com.azure.core.models.CloudEvent; +import com.azure.core.util.BinaryData; +import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.messaging.eventgrid.EventGridPublisherClient; +import com.azure.messaging.eventgrid.EventGridPublisherClientBuilder; +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.camel.Message; +import org.apache.camel.support.DefaultProducer; +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EventGridProducer extends DefaultProducer { + + private static final Logger LOG = LoggerFactory.getLogger(EventGridProducer.class); + + private EventGridPublisherClient<CloudEvent> publisherClient; + + public EventGridProducer(final Endpoint endpoint) { + super(endpoint); + } + + @Override + protected void doStart() throws Exception { + super.doStart(); + + EventGridConfiguration configuration = getConfiguration(); + publisherClient = configuration.getPublisherClient(); + + if (publisherClient == null) { + // Create the client + publisherClient = createEventGridPublisherClient(configuration); + } + } + + @Override + public void process(Exchange exchange) throws Exception { + final Message message = exchange.getMessage(); + final Object body = message.getBody(); + + // Handle both single event and list of events + List<CloudEvent> events = new ArrayList<>(); + + if (body instanceof List) { + List<?> bodyList = (List<?>) body; + for (Object item : bodyList) { + events.add(createCloudEvent(item, message)); + } + } else { + events.add(createCloudEvent(body, message)); + } + + LOG.debug("Publishing {} events to Event Grid", events.size()); + publisherClient.sendEvents(events); + } + + private CloudEvent createCloudEvent(Object data, Message message) { + // Extract CloudEvent properties from headers or use defaults + String eventType = message.getHeader(EventGridConstants.EVENT_TYPE, String.class); + String subject = message.getHeader(EventGridConstants.SUBJECT, String.class); + String id = message.getHeader(EventGridConstants.ID, String.class); + String dataVersion = message.getHeader(EventGridConstants.DATA_VERSION, String.class); + OffsetDateTime eventTime = message.getHeader(EventGridConstants.EVENT_TIME, OffsetDateTime.class); + + // Set defaults if not provided + if (ObjectHelper.isEmpty(eventType)) { + eventType = "Camel.Event"; + } + if (ObjectHelper.isEmpty(subject)) { + subject = "/camel/event"; + } + if (ObjectHelper.isEmpty(id)) { + id = UUID.randomUUID().toString(); + } + if (eventTime == null) { + eventTime = OffsetDateTime.now(); + } + + // Create CloudEvent + CloudEvent event = new CloudEvent("/camel", eventType, BinaryData.fromObject(data), null, null); + event.setSubject(subject); + event.setId(id); + event.setTime(eventTime); + + return event; + } + + private EventGridPublisherClient<CloudEvent> createEventGridPublisherClient(EventGridConfiguration configuration) { + EventGridPublisherClientBuilder builder = new EventGridPublisherClientBuilder() + .endpoint(configuration.getTopicEndpoint()); + + // Configure authentication based on credential type + switch (configuration.getCredentialType()) { + case ACCESS_KEY: + AzureKeyCredential credential = configuration.getAzureKeyCredential(); + if (credential == null && ObjectHelper.isNotEmpty(configuration.getAccessKey())) { + credential = new AzureKeyCredential(configuration.getAccessKey()); + } + if (credential == null) { + throw new IllegalArgumentException("Access key or Azure key credential must be provided"); + } + builder.credential(credential); + break; + case TOKEN_CREDENTIAL: + if (configuration.getTokenCredential() == null) { + throw new IllegalArgumentException("Token credential must be provided"); + } + builder.credential(configuration.getTokenCredential()); + break; + case AZURE_IDENTITY: + builder.credential(new DefaultAzureCredentialBuilder().build()); + break; + default: + throw new IllegalArgumentException("Unknown credential type: " + configuration.getCredentialType()); + } + + return builder.buildCloudEventPublisherClient(); + } + + @Override + protected void doStop() throws Exception { + // EventGridPublisherClient doesn't need explicit cleanup + publisherClient = null; + super.doStop(); + } + + @Override + public EventGridEndpoint getEndpoint() { + return (EventGridEndpoint) super.getEndpoint(); + } + + public EventGridConfiguration getConfiguration() { + return getEndpoint().getConfiguration(); + } +} diff --git a/components/camel-azure/camel-azure-eventgrid/src/main/resources/META-INF/services/org/apache/camel/component/azure-eventgrid b/components/camel-azure/camel-azure-eventgrid/src/main/resources/META-INF/services/org/apache/camel/component/azure-eventgrid new file mode 100644 index 000000000000..077d7282e87b --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/main/resources/META-INF/services/org/apache/camel/component/azure-eventgrid @@ -0,0 +1 @@ +class=org.apache.camel.component.azure.eventgrid.EventGridComponent diff --git a/components/camel-azure/camel-azure-eventgrid/src/test/java/org/apache/camel/component/azure/eventgrid/EventGridComponentTest.java b/components/camel-azure/camel-azure-eventgrid/src/test/java/org/apache/camel/component/azure/eventgrid/EventGridComponentTest.java new file mode 100644 index 000000000000..dc67591aa2ca --- /dev/null +++ b/components/camel-azure/camel-azure-eventgrid/src/test/java/org/apache/camel/component/azure/eventgrid/EventGridComponentTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.camel.component.azure.eventgrid; + +import com.azure.identity.DefaultAzureCredential; +import com.azure.identity.DefaultAzureCredentialBuilder; +import org.apache.camel.ResolveEndpointFailedException; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class EventGridComponentTest extends CamelTestSupport { + + @Test + public void testCreateEndpointWithNoTopicEndpoint() { + ResolveEndpointFailedException exception = assertThrows(ResolveEndpointFailedException.class, + () -> context.getEndpoint("azure-eventgrid:?accessKey=string")); + + assertTrue(exception.getMessage().contains("Topic endpoint must be specified")); + } + + @Test + public void testCreateEndpointWithNoCredentials() { + final String expectedErrorMessage + = "Azure EventGrid AccessKey, AzureKeyCredential, TokenCredential or Azure Identity must be specified"; + + ResolveEndpointFailedException exception = assertThrows(ResolveEndpointFailedException.class, + () -> context.getEndpoint("azure-eventgrid:https://mytopic.eventgrid.azure.net/api/events")); + + assertTrue(exception.getMessage().contains(expectedErrorMessage)); + } + + @Test + public void testCreateEndpointWithAccessKey() { + final String uri = "azure-eventgrid:https://mytopic.eventgrid.azure.net/api/events?accessKey=dummyKey"; + + final EventGridEndpoint endpoint = context.getEndpoint(uri, EventGridEndpoint.class); + + assertNotNull(endpoint); + assertEquals("https://mytopic.eventgrid.azure.net/api/events", endpoint.getConfiguration().getTopicEndpoint()); + assertEquals("dummyKey", endpoint.getConfiguration().getAccessKey()); + assertEquals(CredentialType.ACCESS_KEY, endpoint.getConfiguration().getCredentialType()); + } + + @Test + public void testCreateEndpointWithAzureIdentity() { + final String uri + = "azure-eventgrid:https://mytopic.eventgrid.azure.net/api/events?credentialType=AZURE_IDENTITY"; + + final EventGridEndpoint endpoint = context.getEndpoint(uri, EventGridEndpoint.class); + + assertNotNull(endpoint); + assertEquals("https://mytopic.eventgrid.azure.net/api/events", endpoint.getConfiguration().getTopicEndpoint()); + assertEquals(CredentialType.AZURE_IDENTITY, endpoint.getConfiguration().getCredentialType()); + } + + @Test + public void testCreateEndpointWithTokenCredential() { + final DefaultAzureCredential tokenCredential = new DefaultAzureCredentialBuilder().build(); + context.getRegistry().bind("tokenCredential", tokenCredential); + + final String uri + = "azure-eventgrid:https://mytopic.eventgrid.azure.net/api/events?tokenCredential=#tokenCredential"; + + final EventGridEndpoint endpoint = context.getEndpoint(uri, EventGridEndpoint.class); + + assertNotNull(endpoint); + assertSame(tokenCredential, endpoint.getConfiguration().getTokenCredential()); + assertEquals(CredentialType.AZURE_IDENTITY, endpoint.getConfiguration().getCredentialType()); + } + + @Test + public void testProducerOnlyComponent() { + final String uri = "azure-eventgrid:https://mytopic.eventgrid.azure.net/api/events?accessKey=dummyKey"; + + final EventGridEndpoint endpoint = context.getEndpoint(uri, EventGridEndpoint.class); + + assertThrows(UnsupportedOperationException.class, () -> endpoint.createConsumer(exchange -> { + })); + } +} diff --git a/components/camel-azure/pom.xml b/components/camel-azure/pom.xml index 8bc7d93ed7cf..adc8c6887bdc 100644 --- a/components/camel-azure/pom.xml +++ b/components/camel-azure/pom.xml @@ -46,6 +46,7 @@ <modules> <module>camel-azure-eventhubs</module> + <module>camel-azure-eventgrid</module> <module>camel-azure-storage-blob</module> <module>camel-azure-storage-datalake</module> <module>camel-azure-storage-queue</module> diff --git a/docs/components/modules/ROOT/examples/json/azure-eventgrid.json b/docs/components/modules/ROOT/examples/json/azure-eventgrid.json new file mode 120000 index 000000000000..f8c8f2b8fd9a --- /dev/null +++ b/docs/components/modules/ROOT/examples/json/azure-eventgrid.json @@ -0,0 +1 @@ +../../../../../../components/camel-azure/camel-azure-eventgrid/src/generated/resources/META-INF/org/apache/camel/component/azure/eventgrid/azure-eventgrid.json \ No newline at end of file diff --git a/parent/pom.xml b/parent/pom.xml index 844d34fe3f09..1e3fc8dc5b83 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -942,6 +942,11 @@ <artifactId>camel-azure-cosmosdb</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-azure-eventgrid</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-azure-eventhubs</artifactId>
